CountDownLatch和CyclicBarrier

                            CountDownLatch和CyclicBarrier

1. 问题引入

      通过for循环创造多个线程,但是由于主循环中都是顺序执行代码,如何等到多个线程都创建完毕之后,一起启动呢?或者如何等所有线程都执行完毕后,才继续往下执行?

解决方法:

方法1:利用CountDownLatch:锁存器类

创建一个CountDownLatch对象(startSignal,初始计数值为1,在各个线程的任务中需要传入该CountDownLatch对象,在任务的run方法中通过调用CountDownLatch对象的await方法,阻塞本线程,等所有线程都启动之后,通过调用countDown方法,释放开始信号。

创建一个CountDownLatch对象(endSignal,初始计数值为线程数目N,在各个线程的任务中需要传入该CountDownLatch对象,在任务的run方法最后(一般是finally中)通过调用endSignal对象的countDown方法,表示该线程结束了,等所有线程都结束了,那么就会继续往下执行。(当然,开启所有线程后,需要调用endSignal的await方法阻塞主线程)。

方法2:利用CyclicBarrier类:循环屏障类

2. CountDownLatch介绍

(1)、构造方法

CountDownLatch(int count)    Constructs a CountDownLatch initialized with the given count.

       给定一个初始化的数量,创建一个CountDownLatch对象。

(2)、实例方法

一共5个:await()await(timeout)countDown()getCount()toString()

主要方法有两个:await和countDown。

        await(); // 调用此方法会一直阻塞当前线程,不会向下执行,直到计数值为0的时候该线程才会继续向下执行;使当前线程在锁存器倒计数至零之前一直等待

       countDown(); // 当前线程调用此方法,就会递减锁存器的计数;如果计数到达零,则释放所有等待的线程,否则计数值减一。

(3)、使用场景

示例1介绍

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() { ... }

 }

API示例2介绍

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() { ... }

 }

Memory consistency effects: Until the count reaches zero, actions in a thread prior to calling countDown() happen-before actions following a successful return from a corresponding await() in another thread.

3. CyclicBarrier类介绍

(1)、构造方法2个:

CyclicBarrier(int parties)

          Creates a new CyclicBarrier that will trip when the given number of parties (threads) are waiting upon it, and does not perform a predefined action when the barrier is tripped.

CyclicBarrier(int parties, Runnable barrierAction)

          Creates a new CyclicBarrier that will trip when the given number of parties (threads) are waiting upon it, and which will execute the given barrier action when the barrier is tripped, performed by the last thread entering the barrier.

(2)、实例方法

A、await()  、await(long timeout, TimeUnit unit)

和CountDownLatch的相似,但是返回类型不一样。

int   await()        Waits until all parties have invoked await on this barrier.

int   await(long timeout, TimeUnit unit)       Waits until all parties have invoked await on this barrier, or the specified waiting time elapses.

Returns: the arrival index of the current thread, where index getParties() - 1 indicates the first to arrive and zero indicates the last to arrive.

B、getNumberWaiting

int       getNumberWaiting()        Returns the number of parties currently waiting at the barrier.

C、getParties

int       getParties()           Returns the number of parties required to trip this barrier.

D、isBroken

boolean   isBroken()        Queries if this barrier is in a broken state.

E、reset

       void reset()       Resets the barrier to its initial state.

(3)、使用场景

CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个Excel保存了用户所有银行流水,每个Sheet保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,再用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水。

Sample usage: Here is an example of using a barrier in a parallel decomposition design:

 class Solver {

   final int N;

   final float[][] data;

   final CyclicBarrier barrier;

   class Worker implements Runnable {

     int myRow;

     Worker(int row) { myRow = row; }

     public void run() {

       while (!done()) {

         processRow(myRow);//先处理指定行,然后等待

         try {

           barrier.await();

         } catch (InterruptedException ex) {

           return;

         } catch (BrokenBarrierException ex) {

           return;

         }

       }

     }

   }

 

   public Solver(float[][] matrix) {

     data = matrix;

     N = matrix.length;

     Runnable barrierAction =

       new Runnable() { public void run() { mergeRows(...); }};//线程退出之前,合并所有行

     barrier = new CyclicBarrier(N, barrierAction);

 

     List<Thread> threads = new ArrayList<Thread>(N);

     for (int i = 0; i < N; i++) {

       Thread thread = new Thread(new Worker(i));

       threads.add(thread);

       thread.start();

     }

     // wait until done

     for (Thread thread : threads)

       thread.join();

   }

 }

Here, each worker thread processes a row of the matrix then waits at the barrier until all rows have been processed. When all rows are processed the supplied Runnable barrier action is executed and merges the rows. If the merger determines that a solution has been found then done() will return true and each worker will terminate.

注意:barrierAction是在所有线程调用CyclicBarrier.await()方法之后执行,所以可以在await方法之前,执行一定的工作。

If the barrier action does not rely on the parties being suspended when it is executed, then any of the threads in the party could execute that action when it is released. To facilitate this, each invocation of await() returns the arrival index of that thread at the barrier. You can then choose which thread should execute the barrier action, for example:

 if (barrier.await() == 0) {

       // log the completion of this iteration

 }

The CyclicBarrier uses an all-or-none breakage model for failed synchronization attempts: If a thread leaves a barrier point prematurely because of interruption, failure, or timeout, all other threads waiting at that barrier point will also leave abnormally via BrokenBarrierException (or InterruptedException if they too were interrupted at about the same time).(同生共死)

Memory consistency effects: Actions in a thread prior to calling await() happen-before actions that are part of the barrier action, which in turn happen-before actions following a successful return from the corresponding await() in other threads.

4、CyclicBarrier和CountDownLatch的区别

(1)、使用次数不同

CountDownLatch的计数器只能使用一次;而CyclicBarrier的计数器可以使用reset() 方法重置,循环使用,这也是为啥叫做CyclicBarrier的原因。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

(2)、使用上的区别

CyclicBarrier只需要调用await方法,自动记录到达线程的个数;而CountDownLatch需要await和countDown方法结合,await只能阻塞当前线程,而真正启动这些线程需要调用countDown方法。

(3)、CyclicBarrier提供方法较多

CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量;isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。

       输出:true    屏障已经破坏,无法循环使用。

(4)、总结:

        CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值