跳到主要内容

基于 Lock4j 的分布式锁最佳实践

· 阅读需 3 分钟
程序员老熊

盘古开发框架 基于 Lock4j 提供分布式锁。支持注解声明式和 API 编程式对业务代码在分布式环境下加并发锁。

安装相关盘古模块

<parent>
<groupId>com.gitee.pulanos.pangu</groupId>
<artifactId>pangu-parent</artifactId>
<version>latest.version.xxx</version>
<relativePath/>
</parent>

本地配置

为便于理解,本文基于本地配置的方式编写。若改为标准的 Nacos 配置中心模式,请参阅:配置中心 章节。

spring.application.name=pangu-examples-lock-redis-based

# spring-redis
spring.redis.host=localhost
spring.redis.database=1
spring.redis.port=6379
spring.redis.password=

logging.level.root=INFO
logging.level.com.gitee.pulanos.pangu=INFO

基于注解声明

直接在需要缓存的方法上加上 @Lock4j 注解。

@Lock4j

分布式锁注解。

名称默认值说明
name空字符串用于多个方法锁同一把锁 可以理解为锁资源名称,为空则会使用:包名 + 类名 + 方法名。(一般为空即可)
keys空字符串支持 SPEL 表达式,锁的 key = name + keys
acquireTimeout3000获取锁超时时间 单位:毫秒。可以理解为排队时常,超过这个时常就退出排队,抛出获取锁超时异常。
expire30000过期时间 单位:毫秒。主要是防止死锁,建议估计好你锁方法运行时常,正常没有复杂业务的增删改查最多几秒,留有一定冗余,10 秒足够。默认 30 秒是为了兼容绝大部分场景。

代码片段

/**
* 默认获取锁超时 3 秒,30 秒锁过期
*/
@Lock4j
public void doBiz() {
log.info("执行doBiz...");
ThreadUtil.sleep(25*1000);
}

/**
* 配置获取锁超时时间和锁过期时间 支持 SPEL
* @param accountId
*/
@Lock4j(keys = {"#accountId"}, expire = 20000, acquireTimeout = 10000)
public void doBiz2(Long accountId) {
log.info("执行doBiz2...");
ThreadUtil.sleep(20*1000);
}

基于 API 编程

注入 LockTemplate 对象即可使用。如下代码所示。

@Component
public class ApiLockService {

@Autowired
private LockTemplate lockTemplate;

public void apiLock(String userId) {
//... 各种不需要上锁的操作
String lockKey = "lock4j:ApiLockService:apiLock:" + userId;
final LockInfo lockInfo = lockTemplate.lock(lockKey);
//申请锁失败
if (null == lockInfo) {
throw new LockException("业务处理中,请稍后再试...");
}
//申请锁成功
try {
ThreadUtil.sleep(10000L);
log.info("执行 apiLock, 当前线程{}", Thread.currentThread().getName());
} finally {
lockTemplate.releaseLock(lockInfo);
}
}

}

本文相关范例源码