倒计数器: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,则永远到不了栅栏。