天天看點

倒計數器:CountDownLatch | 循環栅欄:CyclicBarrier

倒計數器:CountDownLatch

        CountDownLatch從名字就可以看出其作用:初始化一個計數,然後每次遞減,直至為0,然後觸發一個動作。隻有一個帶參構造器:

public CountDownLatch(int count);
           

        主要提供了以下方法:

// 等待計數器為0
public void await() throws InterruptedException;
// 等待計數器為0,增加逾時限制
public boolean await(long timeout, TimeUnit unit) throws InterruptedException;
// 計數器減1
public void countDown();
           

        CountDownLatch适合檢查事件完成條件,都完成後觸發一系列的動作。demo如下:

public class CountDownLatchDemo implements Runnable {

	static final CountDownLatch end = new CountDownLatch(10);
	static final CountDownLatchDemo demo = new CountDownLatchDemo();

	@Override
	public void run() {
		try {
			// 模拟檢查任務
			Thread.sleep(new Random().nextInt(10) * 1000);
			System.out.println("check complete");
			end.countDown();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		ExecutorService exec = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			exec.submit(demo);
		}
		// 等待檢查
		end.await();
		// 發射火箭
		System.out.println("Fire!");
		exec.shutdown();
	}

}
           

        主線程進入await()時,進入等待狀态。然後當倒計數器為0時,觸發喚醒操作,列印"Fire!"。

循環栅欄:CyclicBarrier

        CyclicBarrier的用法和CountDownLatch非常類似,但是功能更加強大。它可以實作反複的倒計數的功能,例如有多級的狀态檢查就可以使用。提供了2個構造器:

public CyclicBarrier(int parties);
public CyclicBarrier(int parties, Runnable barrierAction);
           

        第一個構造器建立了計數器為n的循環栅欄;第二個構造器在第一個的基礎上增加了到達"栅欄"時,需要觸發的動作(一個線程),系統會自動啟動線程。

        下面給出一個demo:

public class CyclicBarrierDemo {

	public static class Soldier implements Runnable {
		private String soldier;
		private final CyclicBarrier cyclic;

		public Soldier(String soldier, CyclicBarrier cyclic) {
			this.soldier = soldier;
			this.cyclic = cyclic;
		}

		@Override
		public void run() {
			try {
				// 等待所有士兵到齊
				cyclic.await();
				doWork();
				// 等待所有士兵完成工作
				cyclic.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		void doWork() {
			try {
				Thread.sleep(Math.abs(new Random().nextInt() % 10000));
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println(soldier + ": 完成任務");
		}
	}

	public static class BarrierRun implements Runnable {
		boolean flag;
		int N;

		public BarrierRun(boolean flag, int n) {
			this.flag = flag;
			N = n;
		}

		@Override
		public void run() {
			if (flag) {
				System.out.println( N + " 個士兵完成任務");
			} else {
				System.out.println( N + " 個士兵集合完畢");
				flag = true;
			}
		}
	}

	public static void main(String[] args) {
		final int N = 10;
		Thread[] allSoldier = new Thread[N];
		boolean flag = false;
		CyclicBarrier cyclic = new CyclicBarrier(N, new BarrierRun(flag, N));
		
		System.out.println("集合隊伍");
		for (int i = 0; i < 9; i++) {
			System.out.println("士兵" + i + "報到");
			allSoldier[i] = new Thread(new Soldier("士兵" + i, cyclic));
			allSoldier[i].start();
		}
	}
}
           

        從上述代碼可以看出,主線程中建立了一個大小為10的循環栅欄,并且傳入了一個執行動作BarrierRun。此時,建立了10個Soldier對象,并且每個線程内部都調用了cyclic.await()。每一次await()的調用,都會觸發計數器減1。當減為0時,觸發BarrierRun運作。Soldier中有兩個cyclic.await()操作,是以有2個栅欄。

        上面代碼,如果在主線程中的循環次數少于10,則永遠到不了栅欄。

連結: http://moguhu.com/article/detail?articleId=30