SpringBoot Lock4j 分布式锁实战详解
- 发布时间:2026-03-21 04:20:48
- 本文热度:浏览 5 赞 0 评论 0
- 文章标签: Spring Boot Lock4j 分布式锁
- 全文共1字,阅读约需1分钟
SpringBoot 整合 Lock4j 分布式锁使用详解
分布式锁概述
在微服务架构下,服务往往部署在多实例环境中,多个实例可能会并发访问同一份资源。例如库存扣减、订单处理等场景,如果没有机制控制并发,就可能出现数据不一致或超卖问题。分布式锁应运而生,它可以在分布式环境中保证同一时刻只有一个线程对关键资源进行操作,从而保证业务的正确性。
分布式锁的实现方式主要有:
- 基于数据库(如 MySQL 的
SELECT … FOR UPDATE) - 基于缓存(如 Redis 的 SETNX + 过期机制)
- 基于 Zookeeper 的临时节点
- 基于第三方中间件(如 Redisson、Lock4j 等)
Lock4j 是一款轻量级的 Java 分布式锁框架,支持 Redis、Zookeeper、数据库等多种实现方式,提供注解式锁定,适合与 Spring Boot 快速集成。
Lock4j 核心概念
Lock4j 提供的核心功能包括:
-
注解式加锁
使用@Lock4j注解即可对方法加锁,无需手动操作锁对象。 -
锁类型
- 可重入锁(ReentrantLock):同一线程可重复获取,避免死锁。
- 公平锁(FairLock):按请求顺序获取锁,防止线程饥饿。
-
锁超时控制
可设置锁的持有时间,避免因异常导致锁未释放而阻塞业务。 -
锁键动态生成
支持通过 SpEL 表达式动态生成锁键,实现按业务参数加锁。
环境准备
假设使用 Spring Boot 3.x + Lock4j + Redis 的方案,依赖如下:
<dependency>
<groupId>com.github.klx</groupId>
<artifactId>lock4j-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Redis 可使用单机或集群模式,但生产环境推荐使用集群以保证高可用性。
配置 Lock4j
在 application.yml 中配置 Redis 连接:
spring:
redis:
host: localhost
port: 6379
password:
database: 0
lock4j:
redis:
# 默认锁过期时间(毫秒)
expire: 5000
# 等待时间(毫秒),获取锁失败时等待时间
wait-time: 1000
如果需要支持分布式锁集群模式,可配置多个 Redis 节点。
注解使用
Lock4j 提供 @Lock4j 注解,常用属性如下:
key:锁的唯一标识,支持 SpEL 表达式expire:锁过期时间waitTime:尝试获取锁的等待时间timeUnit:时间单位fair:是否使用公平锁
示例代码:
import com.github.klx.lock4j.spring.annotation.Lock4j;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Lock4j(key = "'order:' + #orderId", expire = 5000, waitTime = 1000)
public void createOrder(Long orderId) {
// 模拟库存扣减逻辑
System.out.println("Processing order: " + orderId);
try {
Thread.sleep(2000); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Order processed: " + orderId);
}
}
在上例中,锁的 key 会动态生成为 order:1001 这样的形式,根据业务参数唯一确定。
高级功能
1. 公平锁
公平锁保证锁按请求顺序获取,避免线程饥饿。使用方式:
@Lock4j(key = "'stock:' + #skuId", fair = true, expire = 3000)
public void deductStock(Long skuId) {
// 扣减库存逻辑
}
2. 可重入锁
当同一线程在方法内部多次调用加锁方法时,可重入锁可防止死锁。
@Lock4j(key = "'user:' + #userId")
public void updateProfile(Long userId) {
innerUpdate(userId);
}
@Lock4j(key = "'user:' + #userId")
private void innerUpdate(Long userId) {
// 内部业务操作
}
3. 异常处理
Lock4j 默认在方法抛出异常时会自动释放锁,无需额外处理。若需要自定义异常处理逻辑,可以在 @Lock4j 注解中指定 throwable 属性。
@Lock4j(key = "'order:' + #orderId", throwable = CustomLockException.class)
public void processOrder(Long orderId) {
// 业务逻辑
}
4. 锁键生成策略
为了避免锁键冲突,Lock4j 支持多种动态生成策略:
- 静态字符串:
key = "fixedKey" - 参数拼接:
key = "'order:' + #orderId" - 方法返回值组合:可通过 SpEL 获取方法参数或返回值生成复杂锁键
使用场景示例
场景一:库存扣减
在高并发秒杀场景中,保证库存扣减原子性是核心问题。结合 Lock4j:
@Service
public class SeckillService {
@Lock4j(key = "'seckill:' + #productId", expire = 2000)
public boolean seckill(Long productId) {
int stock = getStock(productId);
if (stock <= 0) return false;
decreaseStock(productId);
return true;
}
}
场景二:订单幂等处理
订单接口可能会被重复调用,加锁可以保证相同订单号只处理一次:
@Lock4j(key = "'order:process:' + #orderNo", expire = 5000)
public void processOrder(String orderNo) {
if (orderExists(orderNo)) return;
createOrder(orderNo);
}
场景三:分布式任务调度
在多节点分布式调度中,需要保证同一任务只被单节点执行:
@Lock4j(key = "'task:' + #taskId", expire = 10000)
public void executeTask(Long taskId) {
runTask(taskId);
}
性能与注意事项
- 锁过期时间:过短可能导致业务未完成锁就被释放,过长可能影响并发效率。通常根据业务耗时设定 1.5~2 倍的安全时间。
- 锁竞争:高并发场景下锁竞争可能成为性能瓶颈,必要时可以优化业务逻辑或拆分锁粒度。
- 可重入性:同一线程多次加锁需使用可重入锁,否则会死锁。
- 异常释放:Lock4j 默认支持异常自动释放锁,但在特殊场景下需确认释放策略。
Lock4j 与 Redisson 对比
| 功能点 | Lock4j | Redisson |
|---|---|---|
| 集成方式 | 注解式 + Spring Boot | API + 注解式 |
| 锁类型 | 可重入锁、公平锁 | 可重入锁、公平锁、读写锁、信号量 |
| 依赖 | Spring Boot + Redis | Spring Boot + Redis |
| 简单业务适用性 | 高 | 高 |
| 高级功能 | 较少 | 丰富(支持更多锁类型) |
Lock4j 更轻量,适合中小型业务场景,而 Redisson 功能丰富,但引入依赖较大。
总结
通过 Lock4j,Spring Boot 应用可以轻松实现分布式锁,保证关键业务逻辑在分布式环境下的正确性。其注解式设计降低了开发复杂度,支持动态锁键、可重入和公平锁等功能,适合秒杀、订单幂等、分布式调度等高并发业务场景。在使用过程中,需合理设置锁过期时间和锁粒度,并结合业务特性优化性能。