在分布式系统中,多个节点对同一资源(如库存、订单、配置)进行并发操作时,如果没有同步机制,可能出现:
-
超卖/重复扣减:多个线程同时修改库存,导致库存为负。
-
重复任务执行:定时任务重复启动。
-
数据冲突:并发写入数据库,造成数据异常。
为此,我们引入“分布式锁”机制,本文将以 Redisson 实现 Redis 分布式锁,实现高性能、高可靠的并发控制。
一、Redisson 简介
Redisson 是一个基于 Redis 的 Java 客户端框架,提供丰富的分布式对象支持:
-
分布式锁(ReentrantLock、ReadWriteLock)
-
分布式集合(Set、List、Map)
-
延迟队列、布隆过滤器
-
支持主从、哨兵、集群
相比原生 SETNX
方案,Redisson 封装了锁超时续约、WatchDog 机制、可重入等特性,更加安全和易用。
二、引入依赖
xml
复制编辑
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.23.1</version> </dependency>
三、配置 Redis 连接
yaml
复制编辑
spring: redis: host: 127.0.0.1 port: 6379 redisson: singleServerConfig: address: redis://127.0.0.1:6379
四、Redisson 自动配置类(可选)
java
复制编辑
@Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); return Redisson.create(config); } }
若使用 redisson-spring-boot-starter,可省略此配置。
五、实现分布式锁代码逻辑
以库存扣减为例:
mbb.eet-china.com/blog/4120085-468426.html
java
复制编辑
@Service public class StockService { @Autowired private RedissonClient redissonClient; // 模拟库存 private AtomicInteger stock = new AtomicInteger(10); public String deductStock(String productId) { String lockKey = "lock:stock:" + productId; RLock lock = redissonClient.getLock(lockKey); boolean isLocked = false; try { // 尝试加锁(最多等待3秒,锁自动释放时间10秒) isLocked = lock.tryLock(3, 10, TimeUnit.SECONDS); if (isLocked) { if (stock.get() > 0) { stock.decrementAndGet(); return "扣减成功,剩余:" + stock.get(); } else { return "库存不足"; } } else { return "系统繁忙,请稍后重试"; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); return "加锁失败"; } finally { if (isLocked && lock.isHeldByCurrentThread()) { lock.unlock(); // 释放锁 } } } }
六、控制器测试接口
bbs.gongkong.com/d/202507/941307/941307_1.shtml
java
复制编辑
@RestController @RequestMapping("/api/stock") public class StockController { @Autowired private StockService stockService; @GetMapping("/deduct") public String deduct(@RequestParam String productId) { return stockService.deductStock(productId); } }
你可以使用压测工具(如 JMeter、Postman)模拟并发请求。
七、常见使用方式
使用方式 | 示例代码 |
---|---|
获取锁(自动续期) | lock.lock(); |
可重入锁 | 同一线程可重复获取 |
读写锁 | getReadWriteLock().readLock().lock(); |
公平锁(FIFO) | getFairLock() |
尝试锁(带超时等待) | tryLock(5, 10, TimeUnit.SECONDS) |
自动释放锁(无需 unlock) | lock.lock(10, TimeUnit.SECONDS); |
bbs.eeworld.com.cn/thread-1320007-1-1.html
八、避免死锁的机制:WatchDog
当你使用:
club.mscbsc.com/t731572p1.html
java
复制编辑
lock.lock(); // 不传超时时间
Redisson 会启动 WatchDog 看门狗机制:
-
默认锁有效期为 30 秒
-
若线程未释放,WatchDog 每 10 秒自动续约
-
避免因业务执行时间过长导致锁提前释放
若使用 lock.lock(10, TimeUnit.SECONDS)
,将不会启动看门狗机制,锁在 10 秒后自动释放。
九、分布式锁常见应用场景
场景 | 描述 |
---|---|
库存扣减 | 多线程/多节点下防止超卖 |
秒杀系统 | 抢购入口防刷,商品扣减互斥 |
订单防重复提交 | 同一个用户提交按钮多次点击 |
定时任务单点执行 | 保证同一任务不被多个节点同时执行 |
防止缓存击穿 | 高并发时热点 Key 加锁处理 |
scarbbs.com/thread-527191-1-1.html
十、分布式锁与本地锁的对比
特性 | 本地锁(synchronized / ReentrantLock ) | Redis 分布式锁 |
---|---|---|
跨节点有效 | ❌ | ✅ |
自动超时释放 | ❌(异常可能死锁) | ✅(支持 WatchDog) |
可重入 | ✅ | ✅ |
性能 | 高(进程内) | 稍低(网络 + Redis 开销) |
场景 | 单机并发控制 | 分布式场景必选 |
blog.itpub.net/70045253/viewspace-3089609
十一、注意事项与最佳实践
建议 | 说明 |
---|---|
使用 tryLock 而非 lock | 避免死锁风险,添加超时时间 |
锁粒度控制 | 锁 Key 要细化,如 lock:stock:sku123 |
锁释放前判断线程身份 | lock.isHeldByCurrentThread() 保护性释放 |
避免长时间持有锁 | 拆分逻辑或异步处理,提升系统并发能力 |
多操作需封装事务内 | 若涉及数据库更新,考虑放入事务中 |
mbb.eet-china.com/blog/4120314-468425.html
blog.51cto.com/u_17434514/14029866
十二、总结
本文完整实现了基于 Redisson 的分布式锁机制:
-
Redisson 配置与连接 Redis
-
使用
RLock
实现锁获取与释放 -
封装库存扣减案例
-
支持自动续期、超时释放等能力
-
介绍了 WatchDog、可重入、公平锁等功能
分布式锁是微服务系统中控制并发冲突的基础能力,Redisson 提供了一套完备、安全、高性能的实现方案,推荐在业务开发中优先使用