import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; /** * Created by zzq on 2019/6/25. */ public class ZKLock implements Watcher { private ZooKeeper zk; //當前鎖 private String currentLock; //資源名稱 private String lockName; //鎖根節點 private String ROOT_LOCK = "/root_lock"; //鎖的各個資源根節點 private String tmpRootLock; //因爲zookeeper監聽節點狀態會當即返回,因此須要使用CountDownLatch(也可以使用信號量等其餘機制) private CountDownLatch latch; public ZKLock(String zkAddress, String lockName) { this.lockName = "" + System.nanoTime(); try { zk = new ZooKeeper(zkAddress, 30000, this); createZNode(ROOT_LOCK, CreateMode.PERSISTENT); tmpRootLock = ROOT_LOCK + "/" + lockName; createZNode(tmpRootLock, CreateMode.PERSISTENT);//****zk臨時節點下不能建立臨時順序節點 } catch (IOException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } private void createZNode(String node, CreateMode mode) throws KeeperException, InterruptedException { //獲取根節點狀態 Stat stat = zk.exists(node, false); //若是根節點不存在,則建立根節點,根節點類型爲永久節點 if (stat == null) { zk.create(node, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, mode); } } public void lock() { try { //在根節點下建立臨時順序節點,返回值爲建立的節點路徑 currentLock = zk.create(tmpRootLock + "/" + lockName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); //獲取根節點下的全部臨時順序節點,不設置監視器 List<String> children = zk.getChildren(tmpRootLock, false); //對根節點下的全部臨時順序節點進行從小到大排序 children.sort(null); //判斷當前節點是否爲最小節點,若是是則獲取鎖,若不是,則找到本身的前一個節點,監聽其存在狀態 int curIndex = children.indexOf(currentLock.substring(currentLock.lastIndexOf("/") + 1)); if (curIndex != 0) { //獲取當前節點前一個節點的路徑 String prev = children.get(curIndex - 1); //監聽當前節點的前一個節點的狀態,null則節點不存在 Stat stat = zk.exists(tmpRootLock + "/" + prev, true); //此處再次判斷該節點是否存在 if (stat != null) { latch = new CountDownLatch(1); //進入等待鎖狀態 latch.await(); latch = null; } } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } //釋放鎖 public void unlock() { try { //刪除建立的節點 zk.delete(currentLock, -1); List<String> children = zk.getChildren(tmpRootLock, false); if (children.size() == 0) { zk.delete(tmpRootLock, -1); //關閉zookeeper鏈接 zk.close(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } currentLock = null; } @Override public void process(WatchedEvent event) { if (this.latch != null) { latch.countDown(); } } public static void main(String[] args) throws Exception { for (int i = 0; i < 7; i++) { // new Thread(new Runnable() { // @Override // public void run() { // ZKLock lock = new ZKLock("10.10.210.123:2181", "lock"); // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // lock.lock(); // } // }).start(); } ZKLock lock = new ZKLock("10.10.210.123:2181", "L1"); lock.lock(); ZKLock lock1 = new ZKLock("10.10.210.123:2181", "L2"); lock1.lock(); lock1.unlock(); lock.unlock(); String uu = ""; } }