Backend Development 29 min read

Understanding Redisson Distributed Locks: Reentrancy, Fairness, and Watchdog Mechanism

This article explains how Redisson implements distributed locks on Redis, covering basic lock concepts, custom Lua scripts for lock acquisition and release, re‑entrant behavior, the automatic lock‑renewal watchdog, and the design of a fair lock using Redis lists and sorted sets.

Architect
Architect
Architect
Understanding Redisson Distributed Locks: Reentrancy, Fairness, and Watchdog Mechanism

Redisson provides a high‑level Java client for Redis that implements distributed locks with features such as re‑entrancy, automatic lease renewal (watchdog), and optional fair ordering. The article first introduces the Redisson API and compares it with a hand‑crafted lock implementation that uses Lua scripts to manipulate Redis data structures.

For a simple lock, the Lua script checks whether the lock key exists; if not, it creates a hash entry with the thread identifier and sets an expiration. When the same thread re‑enters, the script increments a counter and refreshes the TTL. Unlocking decrements the counter and deletes the key when it reaches zero, publishing a Pub/Sub message to wake waiting threads.

public Boolean tryLock(String lockName, long leaseTime) {
    // set lock with expiration
    redisTemplate.opsForValue().setIfAbsent(key, value, leaseTime, TimeUnit.MILLISECONDS);
}

public void unlock(String lockName, String key) {
    // Lua script to decrement counter and delete if zero
    if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) {
        local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1);
        if (counter == 0) then redis.call('del', KEYS[1]); end;
    }
}

The watchdog mechanism is triggered when a lock is acquired without an explicit lease time. Redisson schedules a Netty timeout that periodically runs a Lua script to extend the lock’s expiration, ensuring the lock remains valid while the owning thread is still active.

local ttl = redis.call('pttl', KEYS[1]);
if (ttl > 0) then redis.call('pexpire', KEYS[1], ARGV[1]); end;

For fair locks, Redisson uses a Redis list (queue) to store waiting thread IDs and a sorted set to track their timeout scores. The Lua script first cleans expired entries, then grants the lock only to the head of the queue, updating scores to preserve FIFO order.

-- Clean expired queue head
while true do
  local first = redis.call('lindex', KEYS[2], 0);
  if not first then break end;
  local timeout = tonumber(redis.call('zscore', KEYS[3], first));
  if timeout <= tonumber(ARGV[4]) then
    redis.call('zrem', KEYS[3], first);
    redis.call('lpop', KEYS[2]);
  else break end;
end
-- Acquire if head matches
if redis.call('exists', KEYS[1]) == 0 and (redis.call('exists', KEYS[2]) == 0 or redis.call('lindex', KEYS[2], 0) == ARGV[2]) then
  redis.call('hset', KEYS[1], ARGV[2], 1);
  redis.call('pexpire', KEYS[1], ARGV[1]);
  return nil;
end

Overall, Redisson abstracts complex Redis operations behind a simple Java API, handling edge cases such as lock renewal, re‑entrancy, and fair ordering, making it a reliable choice for distributed synchronization in backend services.

JavaRedisDistributed LockRedissonwatchdogFair LockReentrant
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.