分布式锁简单实现
- 2024-08-13
分布式锁简单实现
package com.tangyh.basic.cache.lock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Objects;
@Component
public class RedisLock {
/** * redis超时时间 */
public static final int LOCK_EXPIRE = 1000*10;// ms
@Autowiredprivate StringRedisTemplate redisTemplate;
/**
* 分布式锁
*
* @param key key值
* @return 是否获取到
*/
public boolean lock(String key) {
String lock = key;
try {
return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
long expireAt = System.currentTimeMillis() + LOCK_EXPIRE;
Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
if (acquire) {
return true;
} else {
//判断该key上的值是否过期了
byte[] value = connection.get(lock.getBytes());
if (Objects.nonNull(value) && value.length > 0) {
long expireTime = Long.parseLong(new String(value));
if (expireTime < System.currentTimeMillis()) {
// 如果锁已经过期
byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE).getBytes());
// 防止死锁
return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
}
}
}
return false;
});
} finally {
RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
}
}
/**
* 删除锁
*
* @param key
*/
public void delete(String key) {
try {
redisTemplate.delete(key);
} finally {
RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
}
}
}
- 首先,定义了一个
lock
字符串,作为锁的键名。 - 使用
redisTemplate.execute
方法执行 Redis 回调操作,在回调函数中进行分布式锁的获取逻辑。 - 在回调函数内部,计算了锁的过期时间
expireAt
,并通过 Redis 的setNX
命令尝试设置键值对,如果设置成功(即获取到了锁),则返回true
。 - 如果
setNX
返回false
,说明锁已经被其他客户端持有,则尝试检查该锁的过期时间是否已经过期。 - 如果锁已经过期,则使用
getSet
命令更新锁的过期时间,并返回true
,表示成功获取到了锁。这里使用getSet
命令是为了保证更新过期时间的原子性,防止出现竞态条件。 - 如果锁未过期,则返回
false
,表示未能成功获取到锁。
在 delete
方法中,执行删除锁操作:
- 使用
redisTemplate.delete
方法删除指定键名的锁。 - 在
finally
块中,无论是否发生异常,都会执行RedisConnectionUtils.unbindConnection
方法释放 Redis 连接。
这样,无论是获取锁还是删除锁,在操作完成后都会正确释放 Redis 连接,避免了连接资源泄漏的问题。