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<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
RFuture<Boolean> 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<Boolean> 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<RedissonLockEntry> 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<Void> unlockAsync(long threadId) {
RFuture<Boolean> future = unlockInnerAsync(threadId);
CompletionStage<Void> 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
政采云技术
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.
