本文将基于上一篇文章介绍如何通过改进 Redis 分布式锁的实现来解决误删问题。
分布式锁的改进实现
1. 误删问题的原因
在原始实现中,分布式锁通过 Redis 的 setIfAbsent
方法获取锁,并通过 delete
方法释放锁。然而,在某些情况下,可能会出现锁误删的问题,例如:
- 线程 A 获取锁,并执行了一部分操作。
- 在操作过程中,锁的过期时间到了,Redis 自动释放了锁。
- 线程 B 获取到了锁。
- 线程 A 完成操作,调用
unlock
方法,误将线程 B 的锁删除。
2. 改进方案
为了解决这个问题,我们在释放锁之前,增加了对线程标识的校验,只有线程标识与锁中的标识一致时,才进行释放锁的操作。以下是改进后的代码实现:
public class SimpleRedisLock implements Ilock {private String name;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;}private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";@Overridepublic boolean tryLock(long timeoutSec) {// 获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();// 获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec,TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {// 获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();// 判断标识是否一致String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);if (threadId.equals(id)) {// 释放锁stringRedisTemplate.delete(KEY_PREFIX + name);}}
}