分布式锁有多重实现方式,比如数据库 、 redis都可以实现。作为分布式协同工具zookeeper也可以实现分布式锁。
1、实现思路
- 每个客户端往/Locks下创建临时有序节点,/Locks/Lock_ 创建成功后/Locks下面会有每个客户端对应的节点,比如 /Locks/Lock_000001
- 客户端取得/Locks下的子节点,并进行排序,判断在最前面的是否为自己,如果自己的锁节点在第一位,代表获得锁成功。
- 如果自己的锁节点不再第一位,则监听自己前一位的锁节点。例如, 自己锁节点/Lock_000002,那么则监听Lock_000001
- 当前一位锁节点(Lock_000001)对应的客户端执行完成,释放锁,将会触发监听客户端(Lock_0000002) 的逻辑
- 监听客户端重新执行第2步逻辑,判断自己是否获得了锁
监听机制,临时有序节点
public class Mylock{
//zk的连接串
String IP = "127.0.0.1:2181";
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//zk配置信息
Zookeeper zooKeeper;
private static final String LOCK_ROOT_PATH = "/Locks";
private static final String LOCK_NODE_PATH = "Lock_";
private String lockPath;
//打开zookeeper的链接
public MyLock(){
try{
zooKeeper = new ZooKeeper(IP,5000,new watcher(){
@Override
public void process(WatchedEvent event){
if(event.getType==Event.EventType.None){
if(event.getState()==Event.KeeperState.SyncConnected){
System.out.println("连接创建成功~~~");
countDownLatch.countDown();
}
}
}
});
countDownLatch.await();
}catch(Exception e){
e.printStackTrace();
}
}
public void acquireLock() throws Exception{
//创建锁节点
createLock();
//尝试获取锁
attempLock();
}
//创建锁节点
public void createLock() throws Exception{
//1、打开zookeeper连接
//2、创建Locks根节点(创建前先用exit判断是否存在,不存在创建)
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH,false);
if(stat == null){
zooKeeper.create(LOCK_ROOT_PATH,new byte[0],ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
// 3、创建临时有序节点
lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" +LOCK_NODE_PATH,new byte[0] ,
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("创建节点成功~~~" + lockPath);
//进行测试一下~~,看看我们的代码是否有问题~~
}
}
- 尝试获取锁
//监视器对象,监视上一个节点是否被删除\
Watcher watcher = new Watcher(){
@Override
public void proccess(WatcherEvent event){
if(event.getType()==EventType.NodeDeleted){
sychronized(this){
notifyAll();
}
}
}
}
private void attempLock() throws Exception{
//1、获取locks下的所有子节点
List<String> list = zookeeper.getChildren(LOCK_ROOT_PATH,false);
//2、对子节点进行排序
Collections.sort(list);
//3、截取掉前面部分
int index = list.indexOf(lockPath.substring(LOCK_ROOT_PATH.length()+1));
//4、对索引进行判断
if(index == 0) {
System.out.ptintln("说明是第1位,获取锁成功!")
return;
}eles{
//获取上一个节点的路径
String path = list.get(index-1);
//监视上一个节点
Stat stat = zookeeper.exists(LOCk_ROOT_PATH+"/"+path,watcher); //监视器对象,监视上一个节点是否被删除
if(stat==null){
//回调,尝试重新获得锁
attempLock();
}else{
//阻塞等待
synchronized(watcher){
watcher.wait();
}
attempLock();
}
}
}
- 释放锁
public void releaseLock() throws Exception{
//删除临时有序节点
zooKeeper.delete(this.lockPath,-1);
zooKeeper.close();
System.out.println("锁已经释放了~~~);
}