一、锁未被释放
问题描述:在高并发情况下,如果线程获取到锁后,由于异常或其他原因没有释放锁,会导致其他线程无法获取到锁,从而影响程序的正常运行。
解决方案:确保在
finally
块中释放锁,以保证锁一定会被释放。代码示例:
public String stockLock() { RLock lock = redissonClient.getLock("stockLock"); try { if (lock.tryLock(10, TimeUnit.SECONDS)) { // 业务逻辑处理 } } finally { lock.unlock(); } return "ok"; }
二、B的锁被A给释放了
问题描述:多个线程或进程使用相同的锁值去释放锁,可能会导致一个线程释放了另一个线程的锁。
解决方案:使用一个唯一标识符(如UUID)作为锁的值,确保只有持有相同标识符的线程才能释放锁。
代码示例:
String uniqueId = UUID.randomUUID().toString(); RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 业务逻辑处理 } finally { if (lock.isHeldByCurrentThread() && lock.getHoldCount() > 0) { lock.unlock(); } } }
三、数据库事务超时
问题描述:在分布式系统中,数据库事务可能会因为执行时间过长而超时,导致锁的释放和业务逻辑执行不一致。
解决方案:避免在分布式锁的获取和释放过程中使用数据库事务,或者手动控制事务的提交和回滚。
代码示例:
@Autowired DataSourceTransactionManager transactionManager; public void processWithLock() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 执行业务逻辑 } finally { lock.unlock(); } } transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); } }
四、锁过期了,业务还没执行完
问题描述:在业务逻辑执行时间较长的情况下,锁可能会在业务逻辑执行完毕前过期,导致其他线程可以获取到锁。
解决方案:使用具有自动续期功能的客户端(如Redisson),它会在锁即将过期时自动续期。
代码示例:
RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 执行业务逻辑 // Redisson会自动续期锁 } finally { lock.unlock(); } }
五、redis主从复制的坑
问题描述:在Redis主从复制模式下,如果主节点宕机,从节点晋升为主节点,可能会导致多个客户端同时持有锁。
解决方案:避免在主从复制模式下使用分布式锁,或者使用Redis Cluster模式来提高锁的安全性。
代码示例:
// 在Redis Cluster模式下使用分布式锁 RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 执行业务逻辑 } finally { lock.unlock(); } }