分布式锁¶
redis¶
setnx¶
非原子操作,SET if Not eXists, 如果不存在,则 SET,具有互斥性。
- 加锁命令:SETNX key value,当键不存在时,对键进行设置操作并返回成功,否则返回失败。KEY 是锁的唯一标识,一般按业务来决定命名。
- 解锁命令:DEL key,通过删除键值对释放锁,以便其他线程可以通过 SETNX 命令来获取锁。
- 锁超时:EXPIRE key timeout, 设置 key 的超时时间,以保证即使锁没有被显式释放,锁也可以在一定时间后自动释放,避免资源被永远锁住。
加锁实现¶
public boolean tryLock_with_set(String key, String UniqueId, int seconds) {
return "OK".equals(jedis.set(key, UniqueId, "NX", "EX", seconds));
}
释放锁实现¶
不能直接用del key这种粗暴的方式,因为直接del key任何客户端都可以进行解锁了,所以解锁时,我们需要判断锁是否是自己的,基于value值来判断。
Lua脚本的方式,尽量保证原子性。
public boolean releaseLock_with_lua(String key,String value) {
String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then " +
"return redis.call('del',KEYS[1]) else return 0 end";
return jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value)).equals(1L);
}
redisson¶
锁续期¶
- 锁自动续期, 如果业务时间长,会自动续上30s
- 加锁业务完成后,不会给锁续期。即时不执行解锁代码, 也可以解锁
超时时间¶
- 如果传递了锁的超时时间, 就发送给redis执行脚本,进行占锁
- 如果未指定锁的超时时间,则使用默认 30 s 【lockWatchDogTimeout】。只要加锁成功,就会启动定时任务,重新给 锁设置过期时间(30 s),定时任务的时间默认是 看门狗的 1/3
redis实现可重入锁¶
使用线程的ThreadLocal变量存储当前线程持有锁的计数。线程获取锁后将Redis中的value保存在ThreadLocal中,同一线程再次尝试获取锁的时候就先将 ThreadLocal 中的 值 与 Redis 的 value 比较,如果相同则表示这把锁所以该线程,即实现可重入锁。
或 redisson 的RLock。
可重入读写锁RReadWriteLockJava对象实现了java.util.concurrent.locks.ReadWriteLock接口。分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。