Zookeeper:實現節點Double Barriers(即兩次限定操作)

1.聲明

當前內容主要用於本人學習和複習,當前內容爲使用兩個標記爲當前的所有線程開啓一起執行,並實現一起結束

  1. 使用workersNode作爲工作節點的主要節點
  2. 所有的工作線程直接向workersNode節點下面的註冊爲子節點,當註冊子節點達到執行數量,則開始工作
  3. 工作線程完畢後直接向finish節點註冊節點,表示當前線程工作已經完成
  4. 通過finish節點的子節點數量達到指定數量則認爲所有的節點都完成工作,即任務完成

2.主要實現

1.首先將所有的監聽事件抽取出來成爲一個抽象類:EventHandler


import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
/**
 * @description 抽象的監視事件的處理
 * @author hy
 * @date 2020-06-07
 */
public abstract class EventHandler implements Watcher {
	protected String node;
	protected ZooKeeper zk;
	protected Object mointer;
	protected int totalWorker;

	public EventHandler(ZooKeeper zk, String node, Object mointer, int totalWorker) {
		this.node = node;
		this.zk = zk;
		this.mointer = mointer;
		this.totalWorker = totalWorker;
	}

	@Override
	public void process(WatchedEvent event) {
			try {
				doProcess(event);
			} catch (KeeperException | InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	}

	abstract void doProcess(WatchedEvent event) throws KeeperException, InterruptedException;
}

2.創建事件FinishWatcher


import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;

/**
 * @description 所有的任務全部完成的監視器
 * @author hy
 * @date 2020-06-07
 */
public class FinishWatcher extends EventHandler {

	public FinishWatcher(ZooKeeper zk, String node, Object mointer, int totalWorker) {
		super(zk, node, mointer, totalWorker);
	}

	@Override
	public void doProcess(WatchedEvent event) {
		// System.out.println("觸發FinishWatcher事件。。。。。。。。。。");
		synchronized (mointer) {
			try {
				List<String> childrenList = zk.getChildren(node, new FinishWatcher(zk, node, mointer, totalWorker));
				if (childrenList.size() == totalWorker) {
					System.out.println("====所有的節點全部完成任務=====");
					mointer.notify();
				}
			} catch (KeeperException | InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

}

3.創建事件ReadyWatcher


import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;

/**
 * @description 工作監視器,用於監視當前的線程是否全部集合完畢
 * @author hy
 * @date 2020-06-07
 */
public class ReadyWatcher extends EventHandler {
	
	public ReadyWatcher(ZooKeeper zk, String node, Object mointer, int totalWorker) {
		super(zk, node, mointer, totalWorker);
	}

	@Override
	public void doProcess(WatchedEvent event) {
		synchronized (mointer) {
			//System.out.println("觸發ReadyWatcher監控事件。。。。。。。。。。");
			try {
				List<String> childrenList = zk.getChildren(node, new ReadyWatcher(zk, node, mointer, totalWorker));
				if (childrenList.size() == totalWorker) {
					System.out.println("====所有的節點準備就緒=====");
					mointer.notify();
				}
			} catch (KeeperException | InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}

4.創建工作者


import java.io.IOException;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * @description 實際的工作者
 * @author hy
 * @date 2020-06-07
 */
public class Worker implements Runnable {
	private ZooKeeper zk;
	private final String workersNode = "/workersNode";// 默認/workerNode
	private String finishNode = "/finish";
	private Object readyMoniter = new Object();
	private Object finishMoniter = new Object();
	private String name;
	private int totalWorker;

	public Worker(String name, int totalWorker) {
		this.name = name;
		this.totalWorker = totalWorker;
		try {
			zk = new ZooKeeper("192.168.1.105", 10000, null);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		try {
			// 等待所有的線程都集合完畢,然後開始執行任務
			readyWait();
			doWork();
			// 開始完成任務,所有人完成任務後退出
			finish();
		} catch (KeeperException | InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 執行之前的所有線程等待操作
	public void readyWait() throws KeeperException, InterruptedException {
		Stat workeNodeExists = null;
		while (true) {
			synchronized (readyMoniter) {
				Stat stat = zk.exists(workersNode, null);
				boolean exists = stat != null;
				if (exists) {
					workeNodeExists = zk.exists(workersNode + "/" + name, null);
					if (workeNodeExists == null) {
						// 開始加入當前的節點下面,稱爲臨時節點的序列化的節點
						zk.create(workersNode + "/" + name, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
								CreateMode.EPHEMERAL);
						workeNodeExists = zk.exists(workersNode + "/" + name, null);
					}
					// 獲取當前執行的總線程數量,即獲取當前的節點下面的子節點的數量
					List<String> childrenList = zk.getChildren(workersNode,
							new ReadyWatcher(zk, workersNode, readyMoniter, totalWorker));
					if (childrenList.size() != totalWorker) {
						System.out.println(name + ":開始進入等待狀態,等待集合");
						readyMoniter.wait();
					} else {
						break;
					}

				}
			}
		}
	}

	// 實際執行任務操作
	public synchronized void doWork() throws KeeperException, InterruptedException {
		Stat workeNodeExists = zk.exists(workersNode + "/" + name, null);
		if (workeNodeExists != null) {
			// 開始執行
			System.out.println(name + ":開始執行任務");
		}

	}

	// 任務執行完畢後的統一操作
	public void finish() throws KeeperException, InterruptedException {
		 Stat finishNodeExists = zk.exists(finishNode, null);
		 if(finishNodeExists==null) {
			 zk.create(finishNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		 }
		// System.out.println("進入finish模塊");
		while (true) {
			// System.out.println("finish的循環中");
			// Thread.sleep(1000L);
			synchronized (finishMoniter) {
				Stat exists2 = zk.exists(finishNode + "/" + name, false);
				// System.out.println("判斷節點" + finishNode + "/" + name + "是否存在");
				if (exists2 == null) {
					// 開始寫入當前的finish節點
					zk.create(finishNode + "/" + name, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
					// System.out.println("判斷節點" + finishNode + "/" + name + "已創建");
				} else {
					System.out.println("所有任務執行完畢。。。");
				}

				List<String> finishList = zk.getChildren(finishNode,
						new FinishWatcher(zk, finishNode, finishMoniter, totalWorker));
				// System.out.println("finish的所有子節點" + finishList);
				if (finishList.size() == totalWorker) {
					// 任務執行完畢
					System.out.println("所有的工作者都執行完畢。。。。");
					break;
				} else {
					/* System.out.println(name + ":進入完結等待狀態"); */
					finishMoniter.wait();
				}
			}
		}

	}


}

5.創建測試類

/**
 * @description 當前內容用於實現當前Zookeeper上面的Doubble Barriers僞代碼
 * @author hy
 * @date 2020-06-07
 */
public class DoubleBarriersTest {

	public static void main(String[] args) throws InterruptedException, IOException {
		// int totalWorker = 5;
		int totalWorker = 5;

		for (int i = 1; i < totalWorker + 1; i++) {
			Thread worker = new Thread(new Worker("worker" + i, totalWorker));
			Thread.sleep(1000l);
			worker.start();
		}

		/*
		 * Thread worker1 = new Thread(new Worker("worker1", totalWorker)); Thread
		 * worker2 = new Thread(new Worker("worker2", totalWorker)); Thread worker3 =
		 * new Thread(new Worker("worker3", totalWorker)); Thread worker4 = new
		 * Thread(new Worker("worker4", totalWorker)); Thread worker5 = new Thread(new
		 * Worker("worker5", totalWorker)); worker1.start(); worker2.start();
		 * worker3.start(); worker4.start(); worker5.start();
		 */

		System.in.read();

	}

}

3.主要測試

1.首先在zookeeper上面創建節點:/workersNode、/finish

2.啓動測試
在這裏插入圖片描述
發現所有的線程都等待到一定的數量時,纔開始執行任務,最後獲取所有線程任務都完畢的通知

4.分析

  1. 當前的通過兩個節點進行限制方式,大概實現了線程們的工作起點和線程們工作結束的終點
  2. 發現當前的執行中實際上就是通過當前的zookeeper的節點是否存在來實現的
  3. 發現的問題就是不能按照順序進行執行

5.總結

1.本人執行的時候發現了一個問題,那就是在多線程環境下的Watcher需要創建(new),不能使用同一個對象(本人使用同一個對象發現當前的watcher會出現警告)

2.由於不知到什麼原因導致線程進行阻塞(wait),並且出現了一直被鎖定無法被喚醒的狀況(最後發現時Watcher沒有new導致的,這說明在多線程環境下,直接使用一個對象好像存在問題,debug發現報錯了)

3.再次複習了jps和jstack的使用,使用jstack很容易看到你的線程狀態,被什麼鎖定,處於沒有解鎖的狀態

以上純屬個人見解,如有問題請聯本人!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章