redis实现分布式锁
java
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final long DEFAULT_EXPIRE = 30_000; // 默认30秒
// 加锁(带UUID防误删)
public String tryLock(String lockKey, long expire) {
String requestId = UUID.randomUUID().toString();
boolean result = redisTemplate.opsForValue().setIfAbsent(LOCK_PREFIX + lockKey, requestId, expire, TimeUnit.MILLISECONDS);
return result ? requestId : null;
}
// 解锁(验证锁持有者身份后删除)
public boolean unlock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
String currentRequestId = redisTemplate.opsForValue().get(key);
if (requestId.equals(currentRequestId)) {
return redisTemplate.delete(key);
}
return false;
}
}
lua脚本实现方式
java
public class RedisDistributedLock {
private RedisTemplate<String, Object> redisTemplate;
private String lockKey;
private String lockValue;
private int expireTime;
// 获取锁的Lua脚本
private static final String ACQUIRE_LOCK_SCRIPT =
"if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
"redis.call('expire', KEYS[1], tonumber(ARGV[2])) " +
"return 1 " +
"else " +
"return 0 " +
"end";
// 释放锁的Lua脚本
private static final String RELEASE_LOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate,
String lockKey, int expireTime) {
this.redisTemplate = redisTemplate;
this.lockKey = lockKey;
this.expireTime = expireTime;
this.lockValue = UUID.randomUUID().toString();
}
/**
* 尝试获取分布式锁
* @return 是否获取成功
*/
public boolean tryLock() {
RedisScript<Long> script = new DefaultRedisScript<>(ACQUIRE_LOCK_SCRIPT, Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(lockKey),
lockValue,
String.valueOf(expireTime));
return result != null && result == 1;
}
/**
* 释放分布式锁
* @return 是否释放成功
*/
public boolean releaseLock() {
RedisScript<Long> script = new DefaultRedisScript<>(RELEASE_LOCK_SCRIPT, Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(lockKey),
lockValue);
return result != null && result == 1;
}
/**
* 带超时的获取锁方法
* @param timeout 超时时间(毫秒)
* @return 是否获取成功
*/
public boolean tryLock(long timeout) {
long startTime = System.currentTimeMillis();
do {
if (tryLock()) {
return true;
}
try {
Thread.sleep(100); // 短暂休眠避免过度竞争
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
} while (System.currentTimeMillis() - startTime < timeout);
return false;
}
}
代码说明:
- 使用RedisTemplate的setIfAbsent方法实现原子加锁操作
- 通过UUID生成唯一请求ID,防止误删其他客户端的锁
- 解锁时通过Lua脚本验证锁持有者身份,确保原子性
- 支持自定义过期时间,防止死锁
- 依赖Spring框架,需配置RedisTemplate Bean