在java中一个进程里面使用synchronized在new出来对象头信息中加锁,如果是静态方法中在加载的类信息中加锁(我们在锁的原理中讲过)。如果使用lock加锁可以自己指定。这些都是在同一个进程空间中的操作。如果在分布式环境中由于程序不在一个进程空间,就没办法使用这些原子性的元素中加锁,我们需要在不同的进程空间中找原子性的元素。这就是分布式锁
基于数据库的分布式锁
该方式通过数据库的唯一索引来实现(比如数据库的主键索引)。当某个进程想要做唯一资源操作时,插入数据表示获得锁,并获得该资源的使用权,其他进程想要获取该资源的使用权时,发现数据库中已经有记录了,就需要等待。当该进程使用完该资源时,删除插入的数据,其他进程发现数据库中没有记录就插入数据表示占用了资源。该实现方式有以下的问题:
锁没有失效时间,同一进程如果释放锁失败,则会造成死锁,其他进程无法再获取资源
如果在进程插入数据的时候增加版本号(乐观锁的实现方式),如果进程操作完成资源回查发现版本号未被更新过则操作资源成功,如果发现版本号被更新过则该进程操作失败
基于redis的分布式锁
我们一个进程想要获取锁,在进程中生成uuid(保证唯一性即可)放入redis中,其他进程想要获得锁资源先判断本进程的uuid是否和redis中的uudi匹配,不匹配说明不能进行资源的操作。redis使用setnx(set if not exit)命令插入键值对。我们可以给该键值对设置超时时间避免死锁的发生。
基于zookeeper实现分布式锁
zookeeper是一个为分布式应用提供一致性服务的工具。它提供一种树形结构的命名空间。
节点的类型分为永久节点和临时节点。永久节点不会因为会话的结束而消失,临时节点随着会话的结束而结束
我们创建一个锁目录lock.在lock下面创建有序的子节点例如:lock/lock-001 lock/lock-002.
客户端获取/lock目录下的子节点列表并判断自己创建的子节点是否是按照序号的最小子节点,如果是则获得锁。否则监听前一个子节点的变更(zkclient).获得子节点的通知后重复判断该节点是否为最小的子节点,重复以往。
会话超时:如果一个子节点的会话超时了,因为创建的是临时节点,因此对应的临时节点也会被删除,这样避免了死锁的出现。
为什么我们前面只监听前面一个节点呢?如果监听所有的节点,在节点状态变更时,所有节点都会收到通知,并进行获取锁的操作。这样造成资源的浪费。
不管是我们的java锁还是分布式锁,都有一个原子性的元素作为锁的实体做加锁和解锁的操作。我们需要把这个问题想明白,其他的都是逻辑的问题
标签:加锁,15,lock,session,进程,节点,资源,分布式 From: https://blog.csdn.net/yusongcao/article/details/145324334