用zk原生api实现的锁,大体思想:html
每一个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个惟一的瞬时有序临时节点。 判断是否获取锁的方式很简单,只须要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除便可。能够避免服务宕机致使的锁没法释放,而产生的死锁问题。java
示例:http://www.cnblogs.com/liuyang0/p/6800538.html
来看下Zookeeper可能存在一些问题:git
锁没法释放?使用Zookeeper能够有效的解决锁没法释放的问题,由于在建立锁的时候,客户端会在ZK中建立一个临时节点,一旦客户端获取到锁以后忽然挂掉(Session链接断开),那么这个临时节点就会自动删除掉。其余客户端就能够再次得到锁。github
非阻塞锁?使用Zookeeper能够实现阻塞的锁,客户端能够经过在ZK中建立顺序节点,而且在节点上绑定监听器,一旦节点有变化,Zookeeper会通知客户端,客户端能够检查本身建立的节点是否是当前全部节点中序号最小的,若是是,那么本身就获取到锁,即可以执行业务逻辑了。数据库
不可重入?使用Zookeeper也能够有效的解决不可重入的问题,客户端在建立节点的时候,把当前客户端的主机信息和线程信息直接写入到节点中,下次想要获取锁的时候和当前最小的节点中的数据比对一下就能够了。若是和本身的信息同样,那么本身直接获取到锁,若是不同就再建立一个临时的顺序节点,参与排队。apache
单点问题?使用Zookeeper能够有效的解决单点问题,ZK是集群部署的,只要集群中有半数以上的机器存活,就能够对外提供服务。api
性能上不如使用缓存实现分布式锁。由于每次在建立锁和释放锁的过程当中,都要动态建立、销毁瞬时节点来实现锁功能。ZK中建立和删除节点只能经过Leader服务器来执行,而后将数据同步到全部的Follower机器上。缓存
另外Zookeeper也有可能存在并发问题,只是不常见而已。因为网络抖动,客户端与ZK集群的session链接断了,那么zk觉得客户端挂了,就会删除临时节点,这时候其余客户端就能够获取到分布式锁了,从而致使并发问题。解决方案,zk有重试机制,一旦zk集群检测不到客户端的心跳,就会重试,Curator客户端支持多种重试策略。屡次重试以后还不行的话才会删除临时节点。(因此,选择一个合适的重试策略也比较重要,要在锁的粒度和并发之间找一个平衡。)服务器
有效的解决单点问题,不可重入问题,非阻塞问题以及锁没法释放的问题。实现起来较为简单。网络
数据库 > 缓存 > Zookeeper
Zookeeper >= 缓存 > 数据库
缓存 > Zookeeper >= 数据库
Zookeeper > 缓存 > 数据库
Curator提供的InterProcessMutex是分布式锁的实现。 //获取锁(阻塞,直到抢到锁) public void acquire() Acquire the mutex - blocking until it's available. Note: the same thread can call acquire re-entrantly. Each call to acquire must be balanced by a call to release() //提供带入参方法,支持超时释放 public boolean acquire(long time,TimeUnit unit) Acquire the mutex - blocks until it's available or the given time expires. Note: the same thread can call acquire re-entrantly. Each call to acquire that returns true must be balanced by a call to release() Parameters: time - time to wait unit - time unit Returns: true if the mutex was acquired, false if not // 释放锁 public void release()
官方提供的示例: