Using Redisson for Distributed Locks in Java: Configuration, Code Samples, and Source‑Code Analysis
This article explains how distributed locks solve data‑consistency problems in clustered environments, introduces Redisson as a Redis‑based locking library, provides Maven and YAML configuration, shows Java code for Redisson setup and test cases, and analyses the underlying Lua scripts and source‑code mechanisms.
Preface
Distributed locks are primarily used to solve data‑consistency problems in clustered or distributed environments. In a single‑machine scenario, thread safety can be achieved with Java constructs such as volatile , ReentrantLock , synchronized , and classes from the java.util.concurrent package.
The main implementation approaches for distributed locks are:
Database‑based locks
Coordination‑system‑based locks (e.g., Zookeeper)
Cache‑based locks, especially Redis‑based locks
1. Using Redisson
Redisson supports single‑node, master‑slave, sentinel, and cluster modes; the following example uses the single‑node mode.
Maven dependencies:
org.springframework.boot
spring-boot-starter-data-redis
2.4.0
org.redisson
redisson
3.16.8YAML configuration (application.yml):
spring:
redis:
# Redis database index (default 0)
database: 0
# Redis server address
host: 127.0.0.1
# Redis server port
port: 6379
# Redis password (empty by default)
password:
jedis:
pool:
# Maximum connections in the pool (negative means no limit)
max-active: 20
# Maximum wait time for a connection (negative means no limit)
max-wait: -1
# Maximum idle connections
max-idle: 10
# Minimum idle connections
min-idle: 0
# Connection timeout (ms)
timeout: 1000Redisson configuration class:
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.password}")
private String redisPassword;
@Value("${spring.redis.port}")
private String port;
@Bean
@ConditionalOnMissingBean
public RedissonClient redissonClient() {
Config config = new Config();
// Single‑server mode – set address and password (if any)
System.out.println(redisHost);
config.useSingleServer().setAddress("redis://" + redisHost + ":" + port);
return Redisson.create(config);
}
}Test case demonstrating a fair lock with high concurrency:
package com.example.aopdemo;
import com.example.aopdemo.springbootaopdemo.SpringBootDemoxzApplication;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* @ClassName RedissonTest
* @Description Redisson test case
* @Author 阿Q
* @Date 2022/11/26
*/
@Slf4j
@SpringBootTest(classes = SpringBootDemoxzApplication.class)
public class RedissonTest {
@Resource
private RedissonClient redissonClient;
@Resource
private ThreadPoolTaskExecutor executor;
// Redisson distributed‑lock key
private static final String LOCK_TEST_KEY = "redisson:lock:test";
int n = 500;
/**
* Distributed lock test case
*/
@Test
public void lockTest() {
// Simulate high‑concurrency requests with a loop + multithreading
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
// Obtain a fair lock (FIFO) for easier testing
RLock fairLock = redissonClient.getFairLock(LOCK_TEST_KEY);
try {
// tryLock(waitTimeout, leaseTime)
boolean lock = fairLock.tryLock(3000, 30, TimeUnit.MILLISECONDS);
if (lock) {
log.info("Thread:" + Thread.currentThread().getName() + " acquired lock");
log.info("Remaining count:{}", --n);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
log.info("Thread:" + Thread.currentThread().getName() + " releasing lock");
// Always unlock
fairLock.unlock();
}
});
}
try {
// Keep the main thread alive for 10 seconds
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}2. Redisson Source Analysis
Redisson relies on Lua scripts, Netty, and various asynchronous Future mechanisms. During lock and unlock operations it also makes use of Redis pub/sub.
Simple Overview of the Lua Scripts (Redisson 3.16.8)
Understanding the lock implementation requires a basic grasp of the Lua scripts used.
Lock Script
KEYS[1] – lock name
ARGV[1] – automatic expiration time (ms, default 30 s)
ARGV[2] – hash field key (uuid+threadId)
-- If the lock does not exist
if (redis.call('exists', KEYS[1]) == 0) then
-- Increment re‑entry count (initially 0)
redis.call('hincrby', KEYS[1], ARGV[2], 1);
-- Set expiration time
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil; -- lock acquired
end
-- If the lock already exists and belongs to the same thread
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; -- re‑entry successful
end
-- Otherwise return remaining TTL (lock acquisition failed)
return redis.call('pttl', KEYS[1]);Conclusion: Only when the script returns nil does the lock succeed.
Unlock Script
KEYS[1] – lock name
KEYS[2] – pub/sub channel channel=redisson_lock__channel:{lock_name}
ARGV[1] – unlock message (0)
ARGV[2] – watchdog renewal time
ARGV[3] – hash field key (uuid+threadId)
-- If the lock does not exist for this thread
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil; -- unlock succeeded
end
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0; -- re‑entry count decreased, lock still held
else
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1; -- lock fully released
end
return nil;Conclusion: Only when the script returns 1 is the lock truly released.
Watchdog Renewal Script
Executed every 10 seconds (30 s / 3) to extend the lock TTL.
KEYS[1] – lock name
ARGV[1] – renewal time (ms)
-- If the lock owned by this thread exists
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('pexpire', KEYS[1], ARGV[1]);
return 1; -- renewal succeeded
end
return 0; -- lock no longer exists, stop renewingSource Code Highlights
Lock acquisition logic (tryLock)
@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
// Subscribe to unlock messages, wait, retry, etc.
// ... (omitted for brevity) ...
return false;
}Internal lock acquisition (tryAcquire)
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}Asynchronous lock acquisition (tryAcquireAsync)
private
RFuture
tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
RFuture
ttlRemainingFuture;
if (leaseTime != -1) {
ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
} else {
ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
}
// Process result, schedule renewal if needed, etc.
// ... (omitted for brevity) ...
return new CompletableFutureWrapper<>(f);
}Unlock logic
@Override
public void unlock() {
try {
get(unlockAsync(Thread.currentThread().getId()));
} catch (RedisException e) {
if (e.getCause() instanceof IllegalMonitorStateException) {
throw (IllegalMonitorStateException) e.getCause();
} else {
throw e;
}
}
}The article then shifts to promotional material for a ChatGPT community, offering free accounts, courses, and interview resources. While not technical, it is part of the original source.
Conclusion
Redisson implements distributed locking by delegating the core lock/unlock operations to concise Lua scripts, leveraging Redis’ atomic commands, pub/sub, and a watchdog renewal mechanism. Understanding these scripts and the surrounding Java wrapper code provides deep insight into building reliable distributed locks in Java applications.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn 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.