Mastering Distributed Locks with Redisson in Spring Boot 2.7
This guide explains how to implement a robust, re‑entrant distributed lock using Redisson in a Spring Boot 2.7 application, covering basic and asynchronous APIs, Maven dependency setup, YAML configuration, custom annotation, AspectJ implementation, and comprehensive test cases.
1. Introduction
Redisson is a Redis Java client that adds an in‑memory data grid, offering a simple way to use Redis for distributed locks. It works on high‑performance asynchronous, lock‑free Java Redis client and Netty.
JDK and Redis Support
JDK: 1.8~21 Redis: 3.0~7.2
Redisson Distributed Lock
Redisson provides a re‑entrant lock that implements java.util.concurrent.locks.Lock. It uses a Pub/Sub channel to notify waiting threads and a watchdog to extend the lock TTL while the holder is alive (default 30 seconds, configurable via lockWatchdogTimeout).
Basic Usage
RLock lock = redisson.getLock("myLock");
lock.lock(); // acquire lock
// acquire lock and auto‑unlock after 10 seconds
lock.lock(10, TimeUnit.SECONDS);
// wait up to 100 seconds to acquire lock, auto‑unlock after 10 seconds
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
// todo
} finally {
lock.unlock();
}
}Asynchronous API
RLock rLock = redisson.getLock(key);
rLock.lock();
RFuture<Boolean> lockFuture = rLock.tryLockAsync(100, 10, TimeUnit.SECONDS);
lockFuture.whenComplete((res, exception) -> {
// todo
rLock.unlockAsync();
});For full details, refer to the official Redisson documentation.
2. Practical Example
2.1 Dependency Management
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.18.0</version>
</dependency>2.2 Configuration File
spring:
redis:
redisson:
file: classpath:redisson.yamlThe redisson.yaml file contains all Redisson settings, for example:
singleServerConfig:
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
password: xxxooo
subscriptionsPerConnection: 5
clientName: null
address: "redis://127.0.0.1:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 24
connectionPoolSize: 64
database: 14
dnsMonitoringInterval: 5000
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.Kryo5Codec> {}
transportMode: "NIO"2.3 Core Class Definition
Custom Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DLock {
/** distributed lock key */
String value() default "";
}The value attribute defines the lock key; if empty, a key is generated from the class and method parameters.
Aspect Definition
@Component
@Aspect
public class DistributedLockAspect {
private final RedissonClient redisson;
public DistributedLockAspect(RedissonClient redisson) {
this.redisson = redisson;
}
@Pointcut("@annotation(lock)")
private void lockpc(DLock lock) {}
@Around("lockpc(lock)")
public Object lockAround(ProceedingJoinPoint pjp, DLock lock) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Object[] args = pjp.getArgs();
String key = lock.value();
if ("".equals(key)) {
key = configKey(signature.getDeclaringType(), method)
.replaceAll("[^a-zA-Z0-9]", "");
} else {
StandardEvaluationContext context = new StandardEvaluationContext();
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(key);
key = expression.getValue(context, String.class);
}
RLock rLock = redisson.getLock(key);
rLock.lock();
try {
return pjp.proceed();
} finally {
rLock.unlock();
}
}
private String configKey(Class<?> targetType, Method method) {
StringBuilder builder = new StringBuilder();
builder.append(targetType.getSimpleName());
builder.append('#').append(method.getName()).append('(');
for (Class<?> param : method.getParameterTypes()) {
builder.append(param.getSimpleName()).append(',');
}
if (method.getParameterTypes().length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.append(')').toString();
}
}Testing
Test method using SpEL to generate a lock key:
@DLock("'person:' + #person.id + ':' + #cateId")
public void create(Person person, Integer cateId) {
System.out.printf("Current thread: %s%n", Thread.currentThread().getName());
// todo
sleep(5);
}JUnit test that spawns multiple threads to invoke create concurrently:
@Test
public void testCreate() {
int size = 5;
CountDownLatch cdl = new CountDownLatch(size);
CountDownLatch nums = new CountDownLatch(size);
Thread[] threads = new Thread[size];
for (int i = 0; i < size; i++) {
final int n = i;
threads[i] = new Thread(() -> {
try { nums.countDown(); nums.await(); } catch (InterruptedException e) { e.printStackTrace(); }
Person person = new Person();
person.setId(2L);
person.setName("张三");
lockService.create(person, 666);
cdl.countDown();
}, "T - " + n);
}
for (Thread t : threads) { t.start(); }
try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); }
}When all threads use the same parameters, they acquire the lock sequentially, producing timestamps like:
When each thread uses a different key (different request parameters), all threads run concurrently, and the Redis keys differ, as shown below:
Note: The aspect does not handle exceptions that may arise during SpEL evaluation.
This completes the article.
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.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.
