Understanding Redisson Distributed Locks: Architecture, Code Walkthrough, and Watchdog Mechanism
This article introduces Redisson, a high‑performance Java Redis client built on Netty, explains its key advantages, and provides a detailed walkthrough of the RedissonLock implementation—including tryLock, blocking lock, unlock logic, associated Lua scripts, and the automatic watchdog renewal mechanism.
Redisson is a high‑performance Redis client built on the Netty communication framework that offers distributed and scalable Java data structures, a rich set of distributed operations, and many utility methods, allowing developers to focus on business logic instead of reinventing common components.
Key advantages include Netty‑based multiplexed I/O for high throughput, native support for Redis cluster and sentinel modes, a variety of distributed objects such as Bloom Filter, BitSet, AtomicLong, HyperLogLog, and several lock implementations (FairLock, non‑fair Lock, and RedLock based on the Redlock algorithm).
The core of RedissonLock is a re‑entrant distributed lock with both blocking and non‑blocking acquisition paths and a watchdog mechanism that automatically renews locks without an explicit lease time.
/** * @param waitTime maximum time to wait for the lock, default -1 * @param leaseTime lock expiration time, default -1 * @param unit * @param threadId * @return */ private RFuture tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) { RFuture acquiredFuture; if (leaseTime > 0) { acquiredFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN); } else { acquiredFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN); } CompletionStage f = acquiredFuture.thenApply(acquired -> { if (acquired) { if (leaseTime > 0) { internalLockLeaseTime = unit.toMillis(leaseTime); } else { scheduleExpirationRenewal(threadId); } } return acquired; }); return new CompletableFutureWrapper<>(f); }
The corresponding Lua script checks whether the lock key exists; if not, it creates a hash entry with the thread identifier, increments the re‑entry count, sets an expiration, and returns null . If the key already exists and is owned by the same thread, it increments the count, refreshes the expiration, and returns null . Otherwise it returns the remaining TTL, indicating the lock is held by another thread.
The blocking lock method first attempts to acquire the lock, subscribes to a release‑notification channel when it fails, and then repeatedly retries while optionally waiting on a semaphore based on the remaining TTL.
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException { long threadId = Thread.currentThread().getId(); Long ttl = tryAcquire(-1, leaseTime, unit, threadId); if (ttl == null) return; CompletableFuture future = subscribe(threadId); pubSub.timeout(future); RedissonLockEntry entry = interruptibly ? commandExecutor.getInterrupted(future) : commandExecutor.get(future); try { while (true) { ttl = tryAcquire(-1, leaseTime, unit, threadId); if (ttl == null) break; if (ttl >= 0) { try { entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { if (interruptibly) throw e; entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } } else { if (interruptibly) entry.getLatch().acquire(); else entry.getLatch().acquireUninterruptibly(); } } } finally { unsubscribe(entry, threadId); } }
Unlocking is performed by a Lua script that first verifies the current thread owns the lock, decrements the re‑entry counter, and either refreshes the expiration (if the counter remains > 0) or deletes the key and publishes an unlock message (if the counter reaches zero). The Java method wraps this script and cancels any active watchdog task.
public RFuture unlockAsync(long threadId) { RFuture future = unlockInnerAsync(threadId); CompletionStage f = future.handle((opStatus, e) -> { cancelExpirationRenewal(threadId); if (e != null) throw new CompletionException(e); if (opStatus == null) { IllegalMonitorStateException cause = new IllegalMonitorStateException( "attempt to unlock lock, not locked by current thread by node id: " + id + " thread-id: " + threadId); throw new CompletionException(cause); } return null; }); return new CompletableFutureWrapper<>(f); }
The watchdog mechanism periodically (default every lockWatchdogTimeout/3 , i.e., every 10 seconds for a 30‑second timeout) runs a Netty timer task that checks if the lock is still held by the thread; if so, it renews the key’s expiration using a Lua script, otherwise it cancels the task.
In summary, Redisson combines Redis, Lua scripting, and Netty to provide a complete distributed solution for Java applications, with a robust implementation of distributed locks, re‑entrant semantics, automatic lease renewal, and a rich set of distributed data structures.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
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.