ReentrantLock 的典型使用场景和实现方法
基本概念
ReentrantLock 是 Java 幑发包中提供的可重入互斥锁,相比 synchronized 关键字提供了更高的灵活性和功能。
典型使用场景
1. 防止重复提交
防止用户重复点击按钮导致的重复业务处理。
2. 缓存双检锁机制
在缓存失效时,防止多个线程同时重建缓存。
3. 资源竞争控制
对共享资源的访问进行精确控制。
4. 定时任务并发控制
确保同一时刻只有一个定时任务实例运行。
实现方法
方法一:在 Service 层直接使用
@Service
public class BusinessService {
private final ReentrantLock lock = new ReentrantLock();
public void processBusinessLogic(String key) {
lock.lock();
try {
// 执行核心业务逻辑
doSomething(key);
} finally {
lock.unlock();
}
}
private void doSomething(String key) {
// 具体业务实现
}
}
方法二:使用静态 Map 管理多个锁
@Service
public class MultiKeyService {
private static final Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
public void processByKey(String key) {
ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
lock.lock();
try {
// 处理特定 key 的业务逻辑
handleBusiness(key);
} finally {
lock.unlock();
}
}
private void handleBusiness(String key) {
// 具体业务实现
}
}
方法三:封装为工具类使用
@Component
public class LockUtil {
private static final Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
public <T> T executeWithLock(String key, Supplier<T> supplier) {
ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
lock.lock();
try {
return supplier.get();
} finally {
lock.unlock();
}
}
public void executeWithLock(String key, Runnable runnable) {
ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
lock.lock();
try {
runnable.run();
} finally {
lock.unlock();
}
}
}
// 使用示例
@Service
public class BusinessService {
@Autowired
private LockUtil lockUtil;
public void processData(String key) {
lockUtil.executeWithLock(key, () -> {
// 执行业务逻辑
doProcess(key);
});
}
}
方法四:使用 tryLock 避免阻塞
@Service
public class TimeoutControlService {
private final ReentrantLock lock = new ReentrantLock();
public boolean processWithTimeout() {
try {
if (lock.tryLock(3, TimeUnit.SECONDS)) {
try {
// 执行业务逻辑
doProcess();
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
private void doProcess() {
// 具体业务实现
}
}
在 Spring Bean 中的注意事项
1. 锁的作用域
@Service
public class SingletonService {
// 实例变量 - 同一个锁对象
private final ReentrantLock lock = new ReentrantLock();
// 每次调用创建新锁 - 锁失效
// private ReentrantLock getLock() { return new ReentrantLock(); }
}
2. 异常处理
@Service
public class ExceptionSafeService {
private final ReentrantLock lock = new ReentrantLock();
public void safeProcess() {
lock.lock();
try {
// 业务逻辑可能会抛出异常
riskyOperation();
} finally {
// 必须在 finally 中释放锁
lock.unlock();
}
}
}
与 @Transactional 结合使用的注意事项
@Service
public class TransactionalLockService {
private final ReentrantLock lock = new ReentrantLock();
// 推荐:先加锁后开启事务
public void correctWay() {
lock.lock();
try {
transactionalMethod();
} finally {
lock.unlock();
}
}
@Transactional
public void transactionalMethod() {
// 数据库操作
}
}
最佳实践总结
- 锁的粒度:根据业务需求合理设计锁的粒度
- 异常安全:始终在 finally 块中释放锁
- 避免死锁:统一加锁顺序,避免嵌套锁
- 性能考虑:对于竞争不激烈的场景,synchronized 可能更合适
- 监控告警:添加锁等待时间监控,及时发现性能瓶颈
ReentrantLock 在 Spring Boot 中主要用于需要更细粒度控制并发访问的场景,相比 synchronized 提供了更多的功能和灵活性。

浙公网安备 33010602011771号