CountDownLatch与CyclicBarrier

CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。latch也即闭锁的意思,CountDownLatch是闭锁的一种实现,FutureTask也可以实现闭锁。

CountDownLatch类只提供了一个构造器:

public   CountDownLatch( int   count) {...};   //参数count为计数值
下面这3个方法是CountDownLatch类中最重要的方法:
public   void   await()  throws   InterruptedException { }; //等待直到count值为0才继续执行
public   boolean   await( long   timeout, TimeUnit unit)  throws   InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public   void   countDown() { };  //将count值减1
例子:
public   class   Test {
      public   static   void   main(String[] args) {   
          final   CountDownLatch latch =  new   CountDownLatch( 2 );
          new   Thread(){
              public   void   run() {
                  try   {
                      System.out.println( "子线程" +Thread.currentThread().getName()+ "正在执行" );
                     Thread.sleep( 3000 );
                     System.out.println( "子线程" +Thread.currentThread().getName()+ "执行完毕" );
                     latch.countDown();
                 catch   (InterruptedException e) {
                     e.printStackTrace();
                 }
              };
          }.start();
          new   Thread(){
              public   void   run() {
                  try   {
                      System.out.println( "子线程" +Thread.currentThread().getName()+ "正在执行" );
                      Thread.sleep( 3000 );
                      System.out.println( "子线程" +Thread.currentThread().getName()+ "执行完毕" );
                      latch.countDown();
                 catch   (InterruptedException e) {
                     e.printStackTrace();
                 }
              };
          }.start();
          try   {
              System.out.println( "等待2个子线程执行完毕..." );
             latch.await();
             System.out.println( "2个子线程已经执行完毕" );
             System.out.println( "继续执行主线程" );
         catch   (InterruptedException e) {
             e.printStackTrace();
         }
      }
}

CyclicBarrier字面意思回环栅栏(zha lan),通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:

public   CyclicBarrier( int   parties, Runnable barrierAction) {}
public   CyclicBarrier( int   parties) {}

  参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。

  然后CyclicBarrier中最重要的方法就是await方法,它有2个重载版本:

public   int   await()  throws   InterruptedException, BrokenBarrierException { };
public   int   await( long   timeout, TimeUnit unit) throws   InterruptedException,BrokenBarrierException,TimeoutException { };

第一个版本比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;

第二个版本是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。

下面举几个例子就明白了:

假若有若干个线程都要进行写数据操作,并且只有所有线程都完成写数据操作之后,这些线程才能继续做后面的事情,此时就可以利用CyclicBarrier了:

public   class   Test {
     public   static   void   main(String[] args) {
         int   N =  4 ;
         CyclicBarrier barrier  =  new   CyclicBarrier(N);
         for ( int   i= 0 ;i<N;i++)
             new   Writer(barrier).start();
     }
     static   class   Writer  extends   Thread{
         private   CyclicBarrier cyclicBarrier;
         public   Writer(CyclicBarrier cyclicBarrier) {
             this .cyclicBarrier = cyclicBarrier;
         }
         @Override
         public   void   run() {
             System.out.println( "线程" +Thread.currentThread().getName()+ "正在写入数据..." );
             try   {
                 Thread.sleep( 5000 );       //以睡眠来模拟写入数据操作
                 System.out.println( "线程" +Thread.currentThread().getName()+ "写入数据完毕,等待其他线程写入完毕" );
                 cyclicBarrier.await();
             catch   (InterruptedException e) {
                 e.printStackTrace();
             } catch (BrokenBarrierException e){
                 e.printStackTrace();
             }
             System.out.println( "所有线程写入完毕,继续处理其他任务..." );
         }
     }
}
输出:
线程Thread-0正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

从上面输出结果可以看出,每个写入线程执行完写数据操作之后,就在等待其他线程写入操作完毕。 当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。 如果说想在所有线程写入操作完之后,进行额外的其他操作可以为CyclicBarrier提供Runnable参数:

public   class   Test {
     public   static   void   main(String[] args) {
         int   N =  4 ;
         CyclicBarrier barrier =  new   CyclicBarrier(N,  new   Runnable() {
             @Override
             public   void   run() {
                 System.out.println( "当前线程"   + Thread.currentThread().getName());
                 try   {
                     Thread.sleep( 10000 );
                 catch   (InterruptedException e) {}
                 System.out.println( "额外任务完成" );
             }
         });
         for   ( int   i =  0 ; i < N; i++)
             new   Writer(barrier).start();
     }
     static   class   Writer  extends   Thread {
         private   CyclicBarrier cyclicBarrier;
 
         public   Writer(CyclicBarrier cyclicBarrier) {
             this .cyclicBarrier = cyclicBarrier;
         }
         @Override
         public   void   run() {
             System.out.println( "线程"   + Thread.currentThread().getName()+  "正在写入数据..." );
             try   {
                 Thread.sleep( 2000 );  // 以睡眠来模拟写入数据操作
                 System.out.println( "线程"   + Thread.currentThread().getName()+  "写入数据完毕,等待其他线程写入完毕" );
                 /*如果有额外任务,当满足往下执行条件时,会先从在该处等待的线程中随机选出一个来执行额外任务,其它线程继续阻塞,
                  * 知道额外任务结束,所有线程(包括执行额外任务的线程)才可以往下执行
                  */
                 cyclicBarrier.await();
                 System.out.println( "所有线程写入完毕,继续处理其他任务..." );
                 Thread.sleep( 3000 );
                 System.out.println( "线程" +Thread.currentThread().getName()+ "结束" );
             catch   (InterruptedException e) {
                 e.printStackTrace();
             catch   (BrokenBarrierException e) {
                 e.printStackTrace();
             }
         }
     }
}
输出:
线程Thread-0正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
当前线程Thread-1
额外任务完成
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
线程Thread-2结束
线程Thread-3结束
线程Thread-0结束
线程Thread-1结束
从结果可看出, 当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable任务,其余线程继续阻塞,直到任务完成,然后全部线程继续往下执行。

CyclicBarrier是可以重用的,看下面这个例子:
public   class   Test {
     public   static   void   main(String[] args) {
         int   N =  4 ;
         CyclicBarrier barrier =  new   CyclicBarrier(N);
         for   ( int   i =  0 ; i < N +  2 ; i++) {
             new   Writer(barrier).start();
         }
         try   {
             Thread.sleep( 25000 );
         catch   (InterruptedException e) {
             e.printStackTrace();
         }
         System.out.println( "CyclicBarrier重用" );
         for   ( int   i =  0 ; i < N +  2 ; i++) {
             new   Writer(barrier).start();
         }
     }
     static   class   Writer  extends   Thread {
         private   CyclicBarrier cyclicBarrier;
         public   Writer(CyclicBarrier cyclicBarrier) {
             this .cyclicBarrier = cyclicBarrier;
         }
         @Override
         public   void   run() {
             System.out.println( "线程"   + Thread.currentThread().getName() +  "正在写入数据..." );
             try   {
                 Thread.sleep( 5000 );  // 以睡眠来模拟写入数据操作
                 System.out.println( "线程"   + Thread.currentThread().getName() +  "写入数据完毕,等待其他线程写入完毕" );
                 cyclicBarrier.await();
             catch   (InterruptedException e) {
                 e.printStackTrace();
             catch   (BrokenBarrierException e) {
                 e.printStackTrace();
             }
             System.out.println(Thread.currentThread().getName() +  "所有线程写入完毕,继续处理其他任务..." );
         }
     }
}
输出:
线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2正在写入数据...
线程Thread-3正在写入数据...
线程Thread-4正在写入数据...
线程Thread-5正在写入数据...
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
Thread-1所有线程写入完毕,继续处理其他任务...
Thread-2所有线程写入完毕,继续处理其他任务...
Thread-3所有线程写入完毕,继续处理其他任务...
线程Thread-5写入数据完毕,等待其他线程写入完毕
Thread-0所有线程写入完毕,继续处理其他任务...
线程Thread-4写入数据完毕,等待其他线程写入完毕
CyclicBarrier重用
线程Thread-6正在写入数据...
线程Thread-7正在写入数据...
线程Thread-8正在写入数据...
线程Thread-9正在写入数据...
线程Thread-10正在写入数据...
线程Thread-11正在写入数据...
线程Thread-6写入数据完毕,等待其他线程写入完毕
线程Thread-9写入数据完毕,等待其他线程写入完毕
线程Thread-8写入数据完毕,等待其他线程写入完毕
线程Thread-7写入数据完毕,等待其他线程写入完毕
Thread-4所有线程写入完毕,继续处理其他任务...
Thread-9所有线程写入完毕,继续处理其他任务...
Thread-5所有线程写入完毕,继续处理其他任务...
Thread-6所有线程写入完毕,继续处理其他任务...
线程Thread-10写入数据完毕,等待其他线程写入完毕
线程Thread-11写入数据完毕,等待其他线程写入完毕
Thread-11所有线程写入完毕,继续处理其他任务...
Thread-10所有线程写入完毕,继续处理其他任务...
Thread-7所有线程写入完毕,继续处理其他任务...
Thread-8所有线程写入完毕,继续处理其他任务...

版权声明:本文为博主原创文章,未经博主允许不得转载。

你可能感兴趣的:(CountDownLatch与CyclicBarrier)