理解分布式锁

在分布式环境下保证不同节点的线程同步执行
简单来说就是多进程对共享资源的同步访问
在单进程中使用synchronize或者lock等方式就可实现同步,这是在一个jvm上的资源同步,而要做到多进程多jvm的资源同步,就要通过分布式锁

条件

1.在任意时刻只有一个客户端可以拥有锁

2.不能发生死锁--就算客户端在拥有锁的时候崩溃,不会导致其他等待的客户端一直拿不到锁

3.加锁与解锁都只能由同一个客户端完成

实现分布式锁几种方法

1.Memcached分布式锁
利用Memcached的add命令。此命令是原子性操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁。
2.Redis分布式锁
和Memcached的方式类似,利用Redis的setnx命令。此命令同样是原子性操作,只有在key不存在的情况下,才能set成功。(setnx命令并不完善,后续会介绍替代方案)
3.Zookeeper分布式锁
利用Zookeeper的顺序临时节点,来实现分布式锁和等待队列。Zookeeper设计的初衷,就是为了实现分布式锁服务的。
4.Chubby
Google公司实现的粗粒度分布式锁服务,底层利用了Paxos一致性算法。
 
作者:程序员小灰
链接:https://juejin.im/post/5b16148a518825136137c8db
来源:掘金

 

redis分布式锁

1.加锁

String result = jedis.set(Key, requestID, "nx", "px", expireTime);
参数解释
  1. key为锁的名称,传自己设定的唯一值key
  2. requestID传本线程名称,用于解锁时确认是否是本线程的锁
  3. "nx"表明只有key不存在时才执行set value,成功返回1,不成功会返回0
  4. "px"表明为key设置过期时间
  5. exprieTime传过期时间
参数意义
(1)key是每个进程执行set的key,是唯一值,也是锁的名称
(2)requestID保证自己解锁时解的是自己的锁
        有一种情况是有一进程A执行较慢导致过期时间到了而还没执行解锁,此时锁就会因为到过期时间自动解除,另一进程B获得锁,然后A完成任务执行解锁,那么A就解了B的锁
        辅助方法是为获得锁的该线程建立一个守护线程,不断给锁增加时间,防止锁过时
(3)"nx"与lockkey共同实现锁,每个进程要获得锁就执行set,只有返回1才能拥有锁,返回0没有锁需等待
(4)"px"与expireTime配合为key设置过期时间后,就算客户端崩溃不能执行解锁,锁也会自动解除

2.解锁

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
使用lua脚本执行解锁--因为判断锁与释放锁不是原子性的,使用lua脚本实现原子性

3.示意图

(一)AB执行set获取锁,其中A获得了锁 
(二)A执行代码
(三)A执行完代码后先判断是否是自己的锁,确认后再释放
(四)B获得锁