Redisson + Lock4j 实现分布式锁:快速上手与核心用法
·
在分布式系统中,多个服务实例可能同时访问共享资源(如数据库、缓存、订单状态等),如何保证操作的原子性和一致性?分布式锁是解决这一问题的核心手段。
本文将带你使用 Redisson 和 Lock4j 快速实现一个高效、可靠的分布式锁,并通过实际测试验证其并发控制能力。整个过程简单直观,适合大多数 Java 开发者快速集成。
一、技术选型:为什么选择 Redisson + Lock4j?
- Redisson:基于 Redis 的 Java 客户端,提供了高性能的分布式锁(支持可重入、公平锁、自动续期等),是分布式锁的工业级实现。
- Lock4j:一个轻量级的分布式锁框架,支持通过注解方式加锁,极大简化了开发。
二者结合,既能享受注解的便捷性,又能利用 Redisson 的稳定性,是 Spring Boot 项目中实现分布式锁的黄金组合。
二、环境准备与依赖引入
在 pom.xml 中添加以下依赖:
<!-- Lock4j 基于 Redisson 的分布式锁 starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>2.2.7</version>
</dependency>
<!-- Redisson Spring Boot Starter -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.29.0</version>
<exclusions>
<exclusion>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-30</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 兼容 Spring Boot 2.7 的 Spring Data Redis 版本 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-27</artifactId>
<version>3.29.0</version>
</dependency>
✅ 提示:如果你使用的是 Spring Boot 3.x,请使用
redisson-spring-data-30。
三、配置 Redis 连接
在 application.yml 中配置 Redis 地址:
spring:
redis:
host: 127.0.0.1
port: 6379
# password: 123456 (如有密码)
Lock4j 和 Redisson 会自动读取配置并初始化,无需额外编码。
四、使用 @Lock4j 注解加锁
只需在需要加锁的方法上添加 @Lock4j 注解即可。
示例代码:
import com.baomidou.lock.annotation.Lock4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {
@Lock4j(
keys = {"#orderId"}, // 锁的 key,支持 SpEL 表达式
expire = 60000, // 锁过期时间:60秒
acquireTimeout = 1000 // 获取锁超时时间:1秒
)
@PostMapping("/test")
public R<String> test(@RequestParam String orderId) {
log.info("【开始】线程 {} 开始处理订单 {}", Thread.currentThread().getName(), orderId);
try {
Thread.sleep(500); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.info("【结束】线程 {} 完成订单 {}", Thread.currentThread().getName(), orderId);
return R.success("操作成功");
}
}
参数说明:
| 参数 | 说明 |
|---|---|
keys | 锁的键名,支持 SpEL 表达式,如 #orderId |
expire | 锁自动释放时间(毫秒),防止死锁 |
acquireTimeout | 获取锁的最长等待时间,超时则抛出异常 |
五、keys 属性的常用格式(简单实用版)
keys 决定了锁的作用范围。以下是几种最常用的写法:
1. 基本参数:锁定单个资源
@Lock4j(keys = {"#orderId"})
public void processOrder(String orderId) { ... }
- 所有传入相同
orderId的请求互斥执行。
2. 组合参数:锁定多个维度
@Lock4j(keys = {"#userId", "#itemId"})
public void addToCart(Long userId, Long itemId) { ... }
- 只有当
userId和itemId都相同时才互斥,防止同一用户重复添加同一商品。
3. 访问对象属性
public class OrderRequest {
private String orderId;
private Long userId;
// getter/setter...
}
@Lock4j(keys = {"#request.orderId"})
public void createOrder(OrderRequest request) { ... }
- 使用对象的某个字段作为锁键。
4. 添加业务前缀(推荐)
@Lock4j(keys = {"'ORDER:' + #orderId"})
public void processOrder(String orderId) { ... }
- 生成的 key 为
ORDER:1001,便于在 Redis 中识别和管理。
六、全局异常处理:友好提示锁失败
当获取锁超时,会抛出 LockFailureException,我们通过全局异常处理器统一处理。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(LockFailureException.class)
public R<String> handleLockFailure(LockFailureException e) {
log.warn("获取锁失败", e);
return R.error("系统繁忙,请稍后重试");
}
@ExceptionHandler(Exception.class)
public R<String> handleException(Exception e) {
log.error("系统异常", e);
return R.error("请求失败,请重试");
}
}
这样用户不会看到“500错误”,而是收到“系统繁忙”的友好提示。
七、并发测试:验证锁的效果
使用 Apifox 或 Postman 发起并发请求:
- 请求地址:
POST /order/test?orderId=1001 - 并发数:4
- 结果:
- 3 个请求成功执行(串行处理)
- 1 个请求返回“系统繁忙,请稍后重试”
✅ 结论:分布式锁生效,同一订单不会被并发处理。
八、总结
使用 Redisson + Lock4j 实现分布式锁,只需 3 步:
- 引入依赖
- 配置 Redis
- 添加
@Lock4j注解
它具有以下优势:
- ✅ 简单:注解驱动,无需手动加锁/释放
- ✅ 安全:基于 Redisson,支持自动续期,防止死锁
- ✅ 灵活:通过
keys精确控制锁的粒度 - ✅ 友好:异常统一处理,提升用户体验
适用于:订单处理、库存扣减、用户幂等、定时任务防重复等场景。
更多推荐
所有评论(0)