它被用来同步一个或者多个任务,轻质它们等待由其他任务执行的一组操作完成。
你可以向 CountDownLatch 对象设置一个初始计数值,任何在这个对象上调用 await() 的方法都将阻塞,直到这个计数值为0。其他任务在结束其工作时,可以在该对象上调用 countDown() 来减小这个数值,这个方法不会阻塞线程。 CountDownLatch 被设计为只触发一次,计数值不能被重置。如果你需要能够重置计数值的版本,则可以使用 CyclicBarrier .
CountDownLatch 的典型用法时将一个程序分成n个互相独立的可解决任务,并创建值为n的CountDownLatch。每当任务完成时,都会在这个锁存储器上调用countDown()方法。等待问题被解决的任务在这个锁存储器上调用await()。将它们自己拦住,直到存储器计数结束。
public class CountDownLatch extends Object A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier. A CountDownLatch is a versatile synchronization tool and can be used for a number of purposes. A CountDownLatch initialized with a count of one serves as a simple on/off latch, or gate: all threads invoking await wait at the gate until it is opened by a thread invoking countDown. A CountDownLatch initialized to N can be used to make one thread wait until N threads have completed some action, or some action has been completed N times. A useful property of a CountDownLatch is that it doesn't require that threads calling countDown wait for the count to reach zero before proceeding, it simply prevents any thread from proceeding past an await until all threads could pass. Sample usage: Here is a pair of classes in which a group of worker threads use two countdown latches: The first is a start signal that prevents any worker from proceeding until the driver is ready for them to proceed; The second is a completion signal that allows the driver to wait until all workers have completed. class Driver { // ... void main() throws InterruptedException { CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(N); for (int i = 0; i < N; ++i) // create and start threads new Thread(new Worker(startSignal, doneSignal)).start(); doSomethingElse(); // don't let run yet startSignal.countDown(); // let all threads proceed doSomethingElse(); doneSignal.await(); // wait for all to finish } } class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { this.startSignal = startSignal; this.doneSignal = doneSignal; } public void run() { try { startSignal.await(); doWork(); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } void doWork() { ... } } Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor. When all sub-parts are complete, the coordinating thread will be able to pass through await. (When threads must repeatedly count down in this way, instead use a CyclicBarrier.) class Driver2 { // ... void main() throws InterruptedException { CountDownLatch doneSignal = new CountDownLatch(N); Executor e = ... for (int i = 0; i < N; ++i) // create and start threads e.execute(new WorkerRunnable(doneSignal, i)); doneSignal.await(); // wait for all to finish } } class WorkerRunnable implements Runnable { private final CountDownLatch doneSignal; private final int i; WorkerRunnable(CountDownLatch doneSignal, int i) { this.doneSignal = doneSignal; this.i = i; } public void run() { try { doWork(i); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } void doWork() { ... } }
public class CountDownLatch extends Object 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。 CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。 CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。 示例用法: 下面给出了两个类,其中一组 worker 线程使用了两个倒计数锁存器: 第一个类是一个启动信号,在 driver 为继续执行 worker 做好准备之前,它会阻止所有的 worker 继续执行。 第二个类是一个完成信号,它允许 driver 在完成所有 worker 之前一直等待。 class Driver { // ... void main() throws InterruptedException { CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(N); for (int i = 0; i < N; ++i) // create and start threads new Thread(new Worker(startSignal, doneSignal)).start(); doSomethingElse(); // don't let run yet startSignal.countDown(); // let all threads proceed doSomethingElse(); doneSignal.await(); // wait for all to finish } } class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { this.startSignal = startSignal; this.doneSignal = doneSignal; } public void run() { try { startSignal.await(); doWork(); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } void doWork() { ... } } 另一种典型用法是,将一个问题分成 N 个部分,用执行每个部分并让锁存器倒计数的 Runnable 来描述每个部分,然后将所有 Runnable 加入到 Executor 队列。当所有的子部分完成后,协调线程就能够通过 await。(当线程必须用这种方法反复倒计数时,可改为使用 CyclicBarrier。) class Driver2 { // ... void main() throws InterruptedException { CountDownLatch doneSignal = new CountDownLatch(N); Executor e = ... for (int i = 0; i < N; ++i) // create and start threads e.execute(new WorkerRunnable(doneSignal, i)); doneSignal.await(); // wait for all to finish } } class WorkerRunnable implements Runnable { private final CountDownLatch doneSignal; private final int i; WorkerRunnable(CountDownLatch doneSignal, int i) { this.doneSignal = doneSignal; this.i = i; } public void run() { try { doWork(i); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } void doWork() { ... } }
package tij; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * Created by HuAoxiang on 2017/4/1 . * */ class TaskPortion implements Runnable{ private static int counter = 0; private final int id = counter++; private static Random random = new Random(47); private final CountDownLatch countDownLatch; TaskPortion(CountDownLatch latch) { this.countDownLatch = latch; } @Override public String toString() { return "working "+id; } @Override public void run() { try { TimeUnit.MILLISECONDS.sleep(random.nextInt(1000)); System.out.println(this+" completed!"); this.countDownLatch.countDown(); } catch (InterruptedException e) { System.out.println("working interrupted!"); e.printStackTrace(); } } } class WaitTask implements Runnable{ private static int counter = 0; private final int id = counter++; private final CountDownLatch countDownLatch; WaitTask(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { try { this.countDownLatch.await(); System.out.println("task waiting passed......"+this); } catch (InterruptedException e) { System.out.println("task waiting interrupted!"+this); e.printStackTrace(); } } @Override public String toString() { return ""+id; } } public class Test2 { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); CountDownLatch countDownLatch = new CountDownLatch(100); for(int i = 0;i<10;i++) service.execute(new WaitTask(countDownLatch)); for(int i = 0;i<100;i++) service.execute(new TaskPortion(countDownLatch)); service.shutdown(); } }
输出结果:
working 87 completed! working 42 completed! working 98 completed! working 36 completed! working 97 completed! working 70 completed! working 84 completed! working 86 completed! working 66 completed! working 94 completed! working 44 completed! working 11 completed! working 21 completed! working 43 completed! working 41 completed! working 77 completed! working 47 completed! working 8 completed! working 9 completed! working 74 completed! working 75 completed! working 57 completed! working 65 completed! working 79 completed! working 0 completed! working 19 completed! working 60 completed! working 15 completed! working 80 completed! working 10 completed! working 96 completed! working 40 completed! working 63 completed! working 23 completed! working 34 completed! working 89 completed! working 29 completed! working 39 completed! working 55 completed! working 52 completed! working 90 completed! working 25 completed! working 88 completed! working 54 completed! working 62 completed! working 28 completed! working 58 completed! working 5 completed! working 51 completed! working 92 completed! working 76 completed! working 22 completed! working 18 completed! working 56 completed! working 7 completed! working 85 completed! working 12 completed! working 1 completed! working 26 completed! working 99 completed! working 13 completed! working 61 completed! working 72 completed! working 71 completed! working 30 completed! working 2 completed! working 59 completed! working 46 completed! working 67 completed! working 24 completed! working 27 completed! working 50 completed! working 91 completed! working 31 completed! working 14 completed! working 38 completed! working 33 completed! working 53 completed! working 3 completed! working 17 completed! working 6 completed! working 95 completed! working 93 completed! working 35 completed! working 81 completed! working 69 completed! working 78 completed! working 73 completed! working 20 completed! working 32 completed! working 4 completed! working 68 completed! working 45 completed! working 82 completed! working 48 completed! working 37 completed! working 64 completed! working 83 completed! working 49 completed! working 16 completed! task waiting passed......0 task waiting passed......3 task waiting passed......2 task waiting passed......4 task waiting passed......1 task waiting passed......5 task waiting passed......7 task waiting passed......6 task waiting passed......9 task waiting passed......8 Process finished with exit code 0
再来一个例子:
package tij; import java.util.concurrent.CountDownLatch; /** * Created by huaox on 2017/4/21. * */ public class TestCyclicBarrier { public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(2); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(i+" "+Thread.currentThread().getName()); } latch.countDown(); } },"a").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 5; i < 10; i++) { System.out.println(i+" "+Thread.currentThread().getName()); } latch.countDown(); } },"b").start(); new Thread(new Runnable() { @Override public void run() { try { latch.await(); for (int i = 5; i < 10; i++) { System.out.println("i'm waiting for you.... "+Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } },"c").start(); } }
输出结果:
0 a 5 b 6 b 7 b 8 b 9 b 1 a 2 a 3 a 4 a i'm waiting for you.... c i'm waiting for you.... c i'm waiting for you.... c i'm waiting for you.... c i'm waiting for you.... c Process finished with exit code 0