package com.lishilin.javaapi.distributlock;
import org.apache.zookeeper.*;
import java.util.List;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class DistributeLock {
private static final String ROOT = "/LOCKS"; //根节点
private ZooKeeper zooKeeper;
private int sessionTimeout; //会话超时时间
private String lockID; //记录锁节点id
private byte[] data = {1,2,3};
private CountDownLatch countDownLatch = new CountDownLatch(1);
public DistributeLock() {
zooKeeper = ZookeeperClient.getInstance();
sessionTimeout = ZookeeperClient.getSessionTimeout();
}
public boolean lock() {
try {
lockID = zooKeeper.create(ROOT+"/",data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(Thread.currentThread().getName()+"->成功创建节点["+lockID+"]去竞争锁");
List<String> children = zooKeeper.getChildren(ROOT, true);
//从小到大排序
SortedSet<String> sortedSet = new TreeSet();
for (String child : children) {
sortedSet.add(ROOT+"/"+child);
}
String first = sortedSet.first();
if (lockID.equals(first)) {
System.out.println(Thread.currentThread().getName()+"当前线程成功获得锁" +
"lock节点为["+lockID+"]");
return true;
}
SortedSet<String> lessThanLockID = sortedSet.headSet(lockID);//获取比lockID小的所有的节点
if(!lessThanLockID.isEmpty()) {
String prevLockID = lessThanLockID.last();
zooKeeper.exists(prevLockID,watchedEvent -> {
if(watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted) {
countDownLatch.countDown();
}
});
countDownLatch.await(sessionTimeout, TimeUnit.MILLISECONDS);//超时或节点被删除
System.out.println(Thread.currentThread().getName()+"当前线程成功获得锁lock节点为["+lockID+"]");
}
return true;
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public boolean unlock() {
System.out.println(Thread.currentThread().getName()+"开始释放锁["+lockID+"]");
try {
zooKeeper.delete(lockID,-1);
System.out.println(lockID+"成功被删除");
return true;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
return false;
}
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(10);
Random random = new Random();
for(int i=0;i<10;i++) {
new Thread(()->{
DistributeLock lock = null;
try {
lock = new DistributeLock();
countDownLatch.countDown();
countDownLatch.await();
lock.lock();
Thread.sleep(random.nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock != null) {
lock.unlock();
}
}
}).start();
}
}
}
package com.lishilin.javaapi.distributlock;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class ZookeeperClient {
private static ZooKeeper zooKeeper;
private static String CONNECTION = "192.168.137.8:2181";
private static int sessionTimeout = 5000;
public static ZooKeeper getZooKeeper() {
return zooKeeper;
}
public static void setZooKeeper(ZooKeeper zooKeeper) {
ZookeeperClient.zooKeeper = zooKeeper;
}
public static String getCONNECTION() {
return CONNECTION;
}
public static void setCONNECTION(String CONNECTION) {
ZookeeperClient.CONNECTION = CONNECTION;
}
public static int getSessionTimeout() {
return sessionTimeout;
}
public static void setSessionTimeout(int sessionTimeout) {
ZookeeperClient.sessionTimeout = sessionTimeout;
}
public static ZooKeeper getInstance() {
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
zooKeeper = new ZooKeeper(CONNECTION,sessionTimeout,(watchedEvent)->{
if (watchedEvent.getState()== Watcher.Event.KeeperState.SyncConnected) {
countDownLatch.countDown();
}
});
} catch (IOException e) {
e.printStackTrace();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return zooKeeper;
}
}
根据zookeeper临时节点有序节点特性实现分布式锁(公平锁)
新来一个线程竞争锁
1、首先将节点插入到zookeeper锁的根节点下(临时有序节点)
2、获得根节点下所有的孩子节点
3、遍历集合从小到大排序
4、判断当前节点是不是集合里最小的节点
4.1如果是获得锁
4.2如果不是执行下面逻辑
5、获取比当前节点小的所有节点的集合
5.1如果非空,获取集合中最后的节点(比当前节点小的最大的节点),监听这个节点,线程阻塞,如果被删除了,当前线程获取锁,超过超时时间也获得锁。
5.2如果为空,没有比当前线程的节点更小的节点了,当前线程获取锁
线程释放锁
直接删除节点即可。zookeeper临时节点的特性是客户端和zookeeper断开时,节点就被删除,如果使用持久节点,会出现客户端出现异常断开时,锁无法释放的结果
这里实现的是公平非重入锁,如果想实现非公平锁和重入锁,可以参考jdk源码JUC包下的ReentrantLock