11、CountDownLatch

java.util.concurrent

Class CountDownLath

 

使一个线程或多个线程等待另外一个线程或多个线程操作的完成。

 

CountDownLath以一个给定的数字初始化。await方法一直阻塞直到当前这个数字由于其他线程运行countDown方法将初始化的数字减为0,阻塞等待的线程才能被唤醒执行。

这是一个一次性的现象,这个数字不能够重置。如果需要一个重复利用的版本,应该考虑使用CyclicBarrier。

 

一个CountDownLatch被初始化N,那么就会使得线程在await那里全部阻塞,直到有N个线程运行了CountDownLatch使得N变成了0,这时候阻塞的线程能够被唤醒执行。

 1  class Driver { 
 2            void main() throws InterruptedException {
 3              CountDownLatch startSignal = new CountDownLatch(1);
 4              CountDownLatch doneSignal = new CountDownLatch(N);
 5 
 6              for (int i = 0; i < N; ++i) // 创建和启动线程
 7                new Thread(new Worker(startSignal, doneSignal)).start();
 8 
 9              doSomethingElse();            
10              startSignal.countDown();      // 让所有的线程处理12              doneSignal.await();           // 等待所有的线程完成
doSomethingOther()
13 } 14 } 15 16 17 class Worker implements Runnable { 18 private final CountDownLatch startSignal; 19 private final CountDownLatch doneSignal; 20 Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { 21 this.startSignal = startSignal; 22 this.doneSignal = doneSignal; 23 } 24 public void run() { 25 try { 26 startSignal.await(); 27 doWork(); 28 doneSignal.countDown(); 29 } catch (InterruptedException ex) {} // return; 30 } 31 32 void doWork() { ... } 33 } 34

程序分析:

上面的程序是一个驱动程序的框架。假设是这么一个流程:驱动刚开始要获得某些资源(doSomethingElse);然后进行一些异步的初始化工作(doWork);最后为最终运行的软件做一些准备(doSomethingOther)。

这三个步骤是同步的。第二个步骤为了提高效率可以用多个线程去进行。有三个步骤,三个步骤要同步,于是有两个同步点。第一个同步点就是要在第一个步骤完成之后,第一个步骤只有一个线程完成,因此设置了

startSingal;第二个同步点在第二个步骤之后,这个步骤的完成需要N个线程,因此设置了doneSingal。

三个步骤在同一个main线程中,虽然第二个步骤可能起步比较早(产生N个线程准备去进行一些初始化工作),但是他们都会阻塞在在一个同步点处,也就是startSingal.await处。当doSomethingElse方法完成之后,调用了

startSingal.countDown方法,此时startSingal的数字被减为0(也表示第一个阶段已经完成),于是N个阻塞与第一个同步点的线程都被唤醒去执行异步初始化工作(doWork)。当然这些初始化工作的时长又长又短,但是

先被执行完的会阻塞于第二个同步点downSingal.await。直到N个线程完成了(doWork),就会执行最后一个步骤。

 

 1  class Driver2 { 
 2                void main() throws InterruptedException {
 3                  CountDownLatch doneSignal = new CountDownLatch(N);
 4                  Executor e = ...
 5 
 6                  for (int i = 0; i < N; ++i) 
 7                    e.execute(new WorkerRunnable(doneSignal, i));
 8 
 9                  doneSignal.await();        
10                }
11              }
12 
13              class WorkerRunnable implements Runnable {
14                private final CountDownLatch doneSignal;
15                private final int i;
16                WorkerRunnable(CountDownLatch doneSignal, int i) {
17                   this.doneSignal = doneSignal;
18                   this.i = i;
19                }
20                public void run() {
21                   try {
22                     doWork(i);
23                     doneSignal.countDown();
24                   } catch (InterruptedException ex) {} return;
25                }
26 
27                void doWork() { ... }
28              }

程序分析:

上面这个程序框架就是一个典型算法“动态规划”的代码框架。它将问题分解成N个子问题,然后每一部分就是一个Runnable任务并且执行完一个任务就会使得数字减一,我们可以

将所有的任务以队列的形式组织到Executor当中。直到所有的子问题全部完成,协调线程会一直处于阻塞状态。

posted on 2015-05-29 17:37  飞机说之代码也疯狂  阅读(189)  评论(0编辑  收藏  举报