基本概念
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 {@Autowiredprivate 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();}}@Transactionalpublic void transactionalMethod() {// 数据库操作}
}
最佳实践总结
- 锁的粒度:根据业务需求合理设计锁的粒度
- 异常安全:始终在 finally 块中释放锁
- 避免死锁:统一加锁顺序,避免嵌套锁
- 性能考虑:对于竞争不激烈的场景,synchronized 可能更合适
- 监控告警:添加锁等待时间监控,及时发现性能瓶颈
ReentrantLock 在 Spring Boot 中主要用于需要更细粒度控制并发访问的场景,相比 synchronized 提供了更多的功能和灵活性。