Understanding Redisson Distributed Locks: Implementation, Reentrancy, Fairness, and Watchdog Mechanism
This article explains the Redisson library’s distributed lock implementation in Java, covering its basic concepts, custom lock creation with Lua scripts, reentrant and fair lock mechanisms, the watchdog renewal process, and practical usage examples with code snippets.
This article provides an in-depth analysis of Redisson, a Java client that builds a distributed in‑memory data grid on top of Redis, and its support for various distributed synchronization primitives.
Redisson Overview
Redisson offers a rich set of distributed objects (BitSet, Set, Map, Queue, etc.) and services (locks, semaphores, publish/subscribe, scheduler, etc.) that simplify Redis usage for Java developers.
Basic Distributed Lock with Lua
A simple lock can be implemented by using SETNX combined with an expiration, but to guarantee atomicity a Lua script is required. The following script acquires the lock and sets a timeout:
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);The corresponding unlock script decrements the counter and removes the key when the count reaches zero:
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
endReentrant Lock (RLock)
Redisson’s RLock implements a re‑entrant lock by storing a hash where the field is a combination of UUID and thread ID and the value is the lock count. The lock acquisition logic uses the same Lua script as above, while unlocking follows the unlock script, ensuring that the same thread can acquire the lock multiple times without deadlock.
Watchdog (Lock Renewal)
If a lock is created without an explicit lease time, Redisson starts a watchdog task that periodically (≈ 1/3 of the lease time) executes a renewal script to extend the expiration, preventing accidental lock release while the owning thread is still working.
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('pexpire', KEYS[1], ARGV[1]);
return 1;
end;
return 0;This renewal runs asynchronously via Netty’s timeout mechanism and reschedules itself until the lock is explicitly released.
Fair Lock (RedissonFairLock)
For FIFO fairness, Redisson uses a Redis List (queue) and a ZSet (timeout set). Each waiting thread pushes its identifier onto the list and adds a score (current time + wait timeout) to the sorted set. The lock acquisition script first cleans expired entries, then grants the lock only to the head of the queue, ensuring first‑come‑first‑served ordering.
-- Clean expired queue head
while true do
local first = redis.call('lindex', KEYS[2], 0);
if first == false 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 lock absent and 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('lpop', KEYS[2]);
redis.call('zrem', KEYS[3], ARGV[2]);
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- Re‑enter if already owned
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- Return remaining TTL for waiting threads
local ttl = redis.call('pttl', KEYS[1]);
return ttl;The fair lock also publishes an unlock message via Redis Pub/Sub so waiting threads are notified when the lock becomes available.
Conclusion
Redisson combines Java concurrency primitives (AQS), Netty’s asynchronous I/O, and Redis data structures to deliver robust distributed locking with features such as re‑entrancy, automatic lease renewal, and FIFO fairness. The library also provides higher‑level constructs like MultiLock and the RedLock algorithm for multi‑node scenarios.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.