以redisson为例
RLock lock = redissonClient.getLock(key);
lock.lock(timeoutSecond, TimeUnit.SECONDS);
原则:1.自己加的锁自己释放,2.锁到期了业务没执行完还需续期
1. 加锁时执行lua脚本
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);"
KEYS[1]
代表加锁的key,ARGV[2]
代表的是加锁的客户端ID,ARGV[1]
代表的锁的生存时间
2. 锁互斥实现
如果其它客户端来加锁,第一个if会失败,然后第二个if,然后再hash结构中判断是否存在客户端2的ID,因为不含有,所以会走最后return返回过期时间,然后客户端2会进入while循环,不停的尝试加锁。
3. watch dog自动延期机制
如果客户端超过生存时间还想持有锁,怎么办?
加锁成功同时会启动一个后台线程检查,如果客户端还持有锁就会不断延长生存时间,默认是10s一次
4. 可重入
进入第二个if,会把value + 1
5. 释放锁
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
"redis.call('del', KEYS[1]); " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; "+
"end; " +
"return nil;"
先把锁里的计数减一,然后再删除
7. redis分布式锁的缺点
master节点宕机,salve变成master,数据还没来得及同步,这样其他客户端就有可能完成加锁。就会导致多个客户端同时持有了锁。
实时内容请关注微信公众号,公众号与博客同时更新:程序员星星