分布式锁简单实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| 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 {
public static final int LOCK_EXPIRE = 1000*10; @Autowiredprivate StringRedisTemplate redisTemplate;
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 { 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()); } }
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 连接,避免了连接资源泄漏的问题。