🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 简介 很多机器去访问,但是一次只能给一台机器访问 ![](https://box.kancloud.cn/4a6e719f9dbd33b0363252f74477d835_894x296.png) # 代码实现 我们在命令行中创建个节点`create /locks ""` 因为代码中没有判断,没有父节点就创建 ~~~ package lock; import org.apache.zookeeper.*; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; public class ClientLock { private static final String hosts = "192.168.33.12:2181,192.168.3.33:2181"; //超过2秒就认为挂了 private static final int sessionTimeOut = 2000; //父节点 private String groupNode = "locks"; //子节点,都是这个名字,只是名字后面的序号不一样 private String subNode = "sub"; private boolean haveLock = false; private ZooKeeper zk = null; //当前client创建的子节点 private volatile String thisPath; //latch就相当于一个对象,当latch.await()方法执行时,线程就会等待 //当latch的count减为0的时候,将会唤醒等待的线程 //让主线程阻塞掉 private CountDownLatch countDownLatch = new CountDownLatch(1); private void connectZookeeper() throws Exception { //一new完就往下走,但是这时候客户端还没完成连接,所以我们要等他创建好 //一旦成功握手这边的process就会回调一次 zk = new ZooKeeper(hosts, sessionTimeOut, new Watcher() { public void process(WatchedEvent watchedEvent) { //SyncConnected同步连接 //回调了并且事件等于连接成功 if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //把计数减少,然后主线程就可以往下走了 countDownLatch.countDown(); } //收到事件通知后的回调函数(应该是我们自己的事件处理逻辑) System.out.println(watchedEvent.getType() + "---" + watchedEvent.getPath()); try { //子节点发生变化 if ((watchedEvent.getType() == Event.EventType.NodeChildrenChanged) && (watchedEvent.getPath().equals("/" + groupNode))) { //获取子节点对父节点进行监听 List<String> childrenNodes = zk.getChildren("/" + groupNode, true); String thisNode = thisPath.substring(("/" + groupNode + "/").length()); //排序,去比较自己是否是最小的id Collections.sort(childrenNodes); if (childrenNodes.indexOf(thisNode) == 0) { //访问共享资源,删除那把锁 doSomething(); //重新注册一把锁 thisPath = zk.create("/"+groupNode+"/"+subNode,null, ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL); } } } catch (Exception e) { e.printStackTrace(); } } }); countDownLatch.await(); //1.程序一进来就先注册一把锁到zk上 //创建子节点,并且这个子节点后面是跟序号的,在子节点的名称后面加上一串数字后缀,避免冲突,而且子节点是短暂的 thisPath = zk.create("/" + groupNode + "/" + subNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); //wait一小会,便于观察 Thread.sleep(new Random().nextInt(1000)); //从zk的锁父目录下,获得所有子节点,并且注册对父节点的监听 List<String> childrenNodes = zk.getChildren("/" + groupNode, true); //如果争抢资源的程序就只有自己,则可以直接去访问共享资源 if (childrenNodes.size() == 1) { doSomething(); thisPath = zk.create("/" + groupNode + "/" + subNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } } //处理业务逻辑,并且在最后释放锁,共享资源的访问写在这里 private void doSomething() throws Exception { try { System.out.println("gain lock: " + thisPath); Thread.sleep(2000); //do something } finally { System.out.println("finished: " + thisPath); //将thisPath删除, 监听thisPath的client将获得通知 //访问完毕后,需要手动删除这个节点 zk.delete(this.thisPath, -1); } } public static void main(String[] args) throws Exception { ClientLock clientLock = new ClientLock(); clientLock.connectZookeeper(); Thread.sleep(Long.MAX_VALUE); } } ~~~