





























1.参考文章:
https://developer.aliyun.com/article/952621
https://www.cnblogs.com/architectforest/p/13140352.html
https://blog.csdn.net/hugejiletuhugejiltu/article/details/149248846
2.需要引入的依赖为:
<properties>
<redisson.version>3.20.1</redisson.version>
</properties>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
3.本地部署的redis为集群配置,所以在application.yml中添加配置:
spring:
redis:
cluster:
nodes: 192.168.201.66:7001,192.168.201.66:7002,192.168.201.66:7003,192.168.201.66:7004,192.168.201.66:7005,192.168.201.66:7006
password: csii@2021
4.RedisService实现分布式锁的实现类:
package com.csii.service;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* @description com.csii.service
* @author: chengyu
* @date: 2026-03-01 14:18
*/
@Service
@Slf4j
public class RedisService {
@Autowired
private RedissonClient redissonClient;
/**
* 获取锁
* 如果锁不可用,则当前线程将无法参与线程调度,并处于休眠状态,直至获取该锁。
* 实现注意事项 锁的实现可能能够检测到锁的错误使用,例如可能导致死锁的调用,并可能在这种情况下抛出(未检查的)异常。
* 该锁实现必须记录这些情况和异常类型。
*/
public void lock(String lockKey) {
try {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
} catch (Exception e) {
log.error("redis service lock error", e);
throw e;
}
}
/**
* 尝试获取锁
* 仅在调用时锁处于空闲状态时才获取该锁。
* 如果锁可用,则获取该锁并立即返回true值。如果锁不可用,则此方法将立即返回false值。
*
* @param lockKey
* @return 返回值:若已获取锁,则返回true;否则返回false
*/
public boolean tryLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock();
}
/**
* 尝试获取锁,给定一个等待时间
* 如果在给定的等待时间内锁处于空闲状态,且当前线程未被中断,则获取该锁。
*
* @param lockKey
* @param waitTime 等待时间,单位s
*
* @return 如果获取了锁,则返回值为true。 如果获取锁的过程中等待且指定的等待时间已过,则返回值为false
*/
public boolean tryLock(String lockKey, long waitTime) {
try {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock(waitTime, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("redis service trylock error", e);
}
return false;
}
/**
* 释放锁
*
* @param lockKey
* @return
*/
public boolean unlock(String lockKey) {
RLock rLock = redissonClient.getLock(lockKey);
if (rLock != null && rLock.isLocked()) {
rLock.unlock();
return true;
}
return false;
}
}
5.测试redis分布式锁的Controller:
package com.csii.controller;
import com.csii.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @description 测试redis分布式锁Controller
* @author: chengyu
* @date: 2026-03-01 14:50
*/
@RestController
@Slf4j
public class TestLockController {
@Autowired
private RedisService redisService;
private static final String LOCK_KEY = "LOCK:EXECUTE:METHOD";
@GetMapping("/testLock")
public void callService() {
Thread t1 = new Thread(() -> method2(), "thread1");
Thread t2 = new Thread(() -> method2(), "thread2");
Thread t3 = new Thread(() -> method2(), "thread3");
t1.start();
t2.start();
t3.start();
}
/**
* 尝试获取到锁才执行,给定一个等待获取锁的时间(2分钟)
*/
public void method2() {
try {
boolean acquire = redisService.tryLock(LOCK_KEY, 120);
if (!acquire) {
log.info("redis service try lock failed, method not execute and quit.");
return;
}
log.info("method {} begin execute...", Thread.currentThread().getName());
Thread.sleep(10000);
log.info("method {} execute over", Thread.currentThread().getName());
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
redisService.unlock(LOCK_KEY);
}
}
/**
* 获取到锁才执行
*/
public void method() {
try {
redisService.lock(LOCK_KEY);
log.info("method {} begin execute...", Thread.currentThread().getName());
Thread.sleep(20000);
log.info("method {} execute over", Thread.currentThread().getName());
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
redisService.unlock(LOCK_KEY);
}
}
}
6.查看执行结果:
2026-03-01 16:10:48.784 INFO 24120 --- [ thread1] com.csii.controller.TestLockController : method thread1 begin execute...
2026-03-01 16:10:58.786 INFO 24120 --- [ thread1] com.csii.controller.TestLockController : method thread1 execute over
2026-03-01 16:10:58.798 INFO 24120 --- [ thread2] com.csii.controller.TestLockController : method thread2 begin execute...
2026-03-01 16:11:08.799 INFO 24120 --- [ thread2] com.csii.controller.TestLockController : method thread2 execute over
2026-03-01 16:11:08.810 INFO 24120 --- [ thread3] com.csii.controller.TestLockController : method thread3 begin execute...
2026-03-01 16:11:18.811 INFO 24120 --- [ thread3] com.csii.controller.TestLockController : method thread3 execute over
可以看到是有序执行的。
因为方法的执行时间是10s,这里如果把获取锁的等待时间改为1s,则会报错,因为获取锁失败了:
2026-03-01 16:12:36.951 INFO 24120 --- [ thread2] com.csii.controller.TestLockController : method thread2 begin execute...
2026-03-01 16:12:37.946 INFO 24120 --- [ thread1] com.csii.controller.TestLockController : redis service try lock failed, method not execute and quit.
2026-03-01 16:12:37.946 INFO 24120 --- [ thread3] com.csii.controller.TestLockController : redis service try lock failed, method not execute and quit.
Exception in thread "thread3" Exception in thread "thread1" java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 2ba0d98b-a983-4fc0-9b0b-1d7feb4b6a29 thread-id: 463
at org.redisson.RedissonBaseLock.lambda$unlockAsync0$6(RedissonBaseLock.java:369)
at java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:822)
at java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:797)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962)
at org.redisson.command.CommandBatchService.lambda$executeAsync$7(CommandBatchService.java:301)
at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760)
at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:736)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962)
at org.redisson.command.RedisCommonBatchExecutor.handleResult(RedisCommonBatchExecutor.java:163)
at org.redisson.command.RedisExecutor.checkAttemptPromise(RedisExecutor.java:552)
at org.redisson.command.RedisExecutor.lambda$execute$5(RedisExecutor.java:195)
at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760)
at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:736)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962)
at org.redisson.client.handler.CommandDecoder.decodeCommandBatch(CommandDecoder.java:325)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:217)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:144)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:120)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:519)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:280)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 2ba0d98b-a983-4fc0-9b0b-1d7feb4b6a29 thread-id: 461
at org.redisson.RedissonBaseLock.lambda$unlockAsync0$6(RedissonBaseLock.java:369)
at java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:822)
at java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:797)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962)
at org.redisson.command.CommandBatchService.lambda$executeAsync$7(CommandBatchService.java:301)
at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760)
at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:736)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962)
at org.redisson.command.RedisCommonBatchExecutor.handleResult(RedisCommonBatchExecutor.java:163)
at org.redisson.command.RedisExecutor.checkAttemptPromise(RedisExecutor.java:552)
at org.redisson.command.RedisExecutor.lambda$execute$5(RedisExecutor.java:195)
at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760)
at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:736)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962)
at org.redisson.client.handler.CommandDecoder.decodeCommandBatch(CommandDecoder.java:325)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:217)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:144)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:120)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:519)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:280)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
2026-03-01 16:12:46.953 INFO 24120 --- [ thread2] com.csii.controller.TestLockController : method thread2 execute over
7.需要注意事项:
在使用 Redisson 分布式锁时,需注意以下事项,以避免潜在问题:
1.锁的粒度设计
锁的粒度应尽可能小,即只锁定需要保护的资源,避免大范围锁定导致并发性能下降。例如,在库存扣减场景中,应按商品 ID 锁定(stock:deduct:1001),而不是锁定整个库存表(stock:deduct),否则不同商品的库存扣减会相互阻塞。
2.避免死锁
尽管 Redisson 的过期机制和 Watch Dog 可减少死锁风险,但仍需注意:
确保在finally块中释放锁,避免业务逻辑异常导致锁未释放。
避免在持有锁的情况下调用外部服务(如 HTTP 请求、消息发送),若外部服务响应缓慢,可能导致锁超时释放。
合理设置锁的持有时间和等待时间,避免线程长时间等待锁或持有锁过久。
3.锁的名称规范
锁的名称应具有唯一性和可读性,建议采用 “业务模块:操作:资源 ID” 的格式(如order:create:10001表示订单创建锁,资源 ID 为订单号),便于问题排查和监控。
4.避免锁的误释放
解锁时必须先检查当前线程是否持有锁(lock.isHeldByCurrentThread()),否则可能释放其他线程持有的锁。例如,若线程 A 获取锁后因超时被释放,线程 B 获取锁,此时线程 A 若未检查直接解锁,会错误释放线程 B 的锁。
--
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。