Unlocking Redisson: How Distributed Locks Work Under the Hood
This article walks through configuring Redisson with Maven, demonstrates a simple distributed lock example using Sentinel, lists the Redis commands and Lua script semantics involved, and provides a detailed source‑code analysis of the RLock interface, tryLock, unlock, and forceUnlock methods.
Maven Configuration
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.0</version>
</dependency>RedissonLock Simple Example
Redisson supports four connection modes – single, master‑slave, Sentinel, and Cluster – and the example uses Sentinel.
Sentinel Configuration
Config config = new Config();
config.useSentinelServers()
.addSentinelAddress("127.0.0.1:6479", "127.0.0.1:6489")
.setMasterName("master")
.setPassword("password")
.setDatabase(0);
RedissonClient redisson = Redisson.create(config);Simple Usage
RLock lock = redisson.getLock("test_lock");
try {
boolean isLock = lock.tryLock();
if (isLock) {
doBusiness();
}
} catch (Exception e) {
// handle
} finally {
lock.unlock();
}Redis Commands Used by the Distributed Lock
EXISTS key – returns 1 if the key exists, otherwise 0.
GETSET key value – sets the key to a new value and returns the old value.
GET key – returns the string value of the key.
DEL key … – deletes one or more keys.
HSET key field value – sets a hash field.
HEXISTS key field – checks if a hash field exists.
HINCRBY key field increment – increments a hash field.
PEXPIRE key milliseconds – sets key expiration in ms.
PUBLISH channel message – publishes a message to a channel.
Lua Script Semantics in Redisson
Redisson executes Redis commands via Lua scripts. Key concepts include redis.call() to run a command, KEYS[1] for the first key argument, ARGV[1] for the first value argument, and the fact that nil and false are treated the same in return values.
Source Code Analysis
RLock Interface
public interface RLock extends Lock, RExpirable {
void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException;
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
void lock(long leaseTime, TimeUnit unit);
void forceUnlock();
boolean isLocked();
boolean isHeldByCurrentThread();
int getHoldCount();
Future<Void> unlockAsync();
Future<Boolean> tryLockAsync();
Future<Void> lockAsync();
Future<Void> lockAsync(long leaseTime, TimeUnit unit);
Future<Boolean> tryLockAsync(long waitTime, TimeUnit unit);
Future<Boolean> tryLockAsync(long waitTime, long leaseTime, TimeUnit unit);
}The interface extends Lock and adds methods such as tryLock with a lease time, which automatically releases the lock after the specified period (default 30 s).
RedissonLock tryLock Implementation
Future<Long> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId) {
internalLockLeaseTime = unit.toMillis(leaseTime);
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_LONG,
"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]);",
Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}Key mappings: KEYS[1] is the lock name (e.g., test_lock), ARGV[1] is the lease time (default 30 s), and ARGV[2] is the thread identifier ( id:threadId). The script creates the lock if it does not exist, increments the re‑entry counter if the same thread already holds it, and returns the remaining TTL otherwise.
RedissonLock unlock Implementation
public void unlock() {
Boolean opStatus = commandExecutor.evalWrite(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end;" +
"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; " +
"end; " +
"return nil;",
Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(Thread.currentThread().getId()));
if (opStatus == null) {
throw new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread");
}
if (opStatus) {
cancelExpirationRenewal();
}
}The script checks existence, verifies the current thread owns the lock, decrements the re‑entry counter, renews the TTL if needed, or deletes the lock and publishes an unlock message.
RedissonLock forceUnlock Implementation
public void forceUnlock() {
get(forceUnlockAsync());
}
Future<Boolean> forceUnlockAsync() {
cancelExpirationRenewal();
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('del', KEYS[1]) == 1) then " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1 " +
"else " +
"return 0 " +
"end",
Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage);
}Forceful unlock simply deletes the lock key and publishes a message; it is used mainly during resource cleanup.
Conclusion
The article provides a concise Redisson distributed‑lock example, explains the underlying Redis commands and Lua scripts, and analyses the relevant source code. Further deep‑dive would require examining Netty, which Redisson uses for asynchronous processing.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
