CountDownLatch与CyclicBarrier

来源:互联网 发布:svm算法推荐 编辑:程序博客网 时间:2024/04/30 08:07

CountDownLatch

      直译过来就是倒计数(CountDown)门闩(Latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程。当计数器count变为0时,所有线程不再等待,同时运行。    

      CountDownLatch的作用和 Thread.join()方法类似,可用于一组线程和另外一组线程的协作。例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作。这些准备工作彼此独立,所以可以并发执行以提高速度。在这个场景下就可以使用 CountDownLatch 协调线程之间的调度了。

     上面说的有点儿抽象,还是用实际例子说明吧。。。

下面模拟的是一个短跑比赛,共3名运动员,那么比赛要开始的话,就必须等到所有的运动员准备完毕。

下面看代码:

   

<span style="font-size:18px;">package test;import java.io.IOException;import java.util.Random;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CountDownLatch;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test4 {        public static void main(String[] args) throws IOException, InterruptedException {          //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去        CountDownLatch begin = new CountDownLatch(3);            ExecutorService executor = Executors.newFixedThreadPool(3);          executor.submit(new Thread(new Runner(begin, "1号选手")));          executor.submit(new Thread(new Runner(begin, "2号选手")));          executor.submit(new Thread(new Runner(begin, "3号选手")));                  executor.shutdown();      }  }    class Runner implements Runnable {      // 当begin为0时,运动员起跑 private CountDownLatch begin;      private String name;        public Runner(CountDownLatch begin, String name) {          super();          this.begin = begin;          this.name = name;      }        @Override      public void run() {          try {              Thread.sleep(1000 * (new Random()).nextInt(8));              System.out.println(name + " 准备好了...");              //count减一,当count为0时,等待结束,所有运动员准备完毕,比赛开始              begin.countDown();            //等待所有运动员准备好            begin.await();          } catch (InterruptedException e) {              e.printStackTrace();          }         System.out.println(name + " 起跑!");          try {Thread.sleep((long)(Math.random()*1000));} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}         System.out.println(name + " end!");      }  }  </span>


输出结果:

2号选手 准备好了...

1号选手 准备好了...

3号选手 准备好了...

3号选手 起跑!

2号选手 起跑!

1号选手 起跑!

3号选手 end!

1号选手 end!

2号选手 end!


        当 begin.await()会阻塞线程,当 begin.countDown()被调用之后,计数器count(为什么是count?看源码)减一,当count为0时,所有线程停止等待。

说完了CountDownLatch,再来看看CyclicBarrier。。。。。。

CyclicBarrier

      CyclicBarrier翻译过来叫循环障碍。它主要的方法就是一个:await()await() 方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。在这之后,如果再次调用 await() 方法,计数就又会变成 N-1,新一轮重新开始,这便是 Cyclic 的含义所在。

      CyclicBarrier的使用并不难,但需要主要它所相关的异常。除了常见的异常,CyclicBarrier.await() 方法会抛出一个独有的 BrokenBarrierException。这个异常发生在当某个线程在等待本 CyclicBarrier 时被中断或超时或被重置时,其它同样在这个CyclicBarrier 上等待的线程便会收到 BrokenBarrierException而停止等待。

还是上面的例子,下面我们用CyclicBarrier实现一下:

package test;import java.io.IOException;import java.util.Random;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test3 {        public static void main(String[] args) throws IOException, InterruptedException {          //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去          //Waits until all parties have invoked await on this barrier.           CyclicBarrier barrier = new CyclicBarrier(3);            ExecutorService executor = Executors.newFixedThreadPool(3);          executor.submit(new Thread(new Runner(barrier, "1号选手")));          executor.submit(new Thread(new Runner(barrier, "2号选手")));          executor.submit(new Thread(new Runner(barrier, "3号选手")));          executor.shutdown();      }  }    class Runner implements Runnable {      // 当barrier为0时,运动员起跑      private CyclicBarrier barrier;        private String name;        public Runner(CyclicBarrier barrier, String name) {         super();          this.barrier = barrier;          this.name = name;      }        @Override      public void run() {          try {              Thread.sleep(1000 * (new Random()).nextInt(8));              System.out.println(name + " 准备好了...");              // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。              barrier.await();          } catch (InterruptedException e) {              e.printStackTrace();          } catch (BrokenBarrierException e) {              e.printStackTrace();          }          System.out.println(name + " 起跑!");          try {Thread.sleep((long)(Math.random()*1000));} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}         System.out.println(name + " end!");      }  }  

输出结果:

1号选手 准备好了...

2号选手 准备好了...

3号选手 准备好了...

3号选手 起跑!

1号选手 起跑!

2号选手 起跑!

2号选手 end!

3号选手 end!

1号选手 end!


功能和结果并没有什么不同


CyclicBarrier和 CountDownLatch 在用法上的不同

        CountDownLatch适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch的主要场景;CyclicBarrier 用于一组或几组线程,还是以刚才短跑比赛为例,刚才只是一组选手比赛,如果我们改成两组呢???这时有两组选手,第一组全部跑完,第二组才能开始。这时候使用CyclicBarrier实现就很容易,因为CyclicBarrier每次count归零以后都会重置,所以可以循环使用,CountDownLatch却不具备这个功能

下面看例子:

    
package test;import java.io.IOException;import java.util.Random;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test5 {        public static void main(String[] args) throws IOException, InterruptedException {          //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去          //Waits until all parties have invoked await on this barrier.           CyclicBarrier barrier = new CyclicBarrier(3);            ExecutorService executor = Executors.newFixedThreadPool(3);          executor.submit(new Thread(new Runner(barrier, "第1组:1号选手")));          executor.submit(new Thread(new Runner(barrier, "第1组:2号选手")));          executor.submit(new Thread(new Runner(barrier, "第1组:3号选手")));        executor.submit(new Thread(new Runner(barrier, "第2组:1号选手")));          executor.submit(new Thread(new Runner(barrier, "第2组:2号选手")));          executor.submit(new Thread(new Runner(barrier, "第2组:3号选手")));        executor.shutdown();      }  }    class Runner implements Runnable {      // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)      private CyclicBarrier barrier;        private String name;        public Runner(CyclicBarrier barrier, String name) {         super();          this.barrier = barrier;          this.name = name;      }        @Override      public void run() {          try {              Thread.sleep(1000 * (new Random()).nextInt(8));              System.out.println(name + " 准备好了...");              // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。              barrier.await();          } catch (InterruptedException e) {              e.printStackTrace();          } catch (BrokenBarrierException e) {              e.printStackTrace();          }          System.out.println(name + " 起跑!");          try {Thread.sleep((long)(Math.random()*1000));} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}         System.out.println(name + " end!");      }  }  

输出结果:

第1组:3号选手 准备好了...

第1组:1号选手 准备好了...

第1组:2号选手 准备好了...

第1组:2号选手 起跑!

第1组:3号选手 起跑!

第1组:1号选手 起跑!

第1组:1号选手 end!

第1组:3号选手 end!

第1组:2号选手 end!

第2组:1号选手 准备好了...

第2组:2号选手 准备好了...

第2组:3号选手 准备好了...

第2组:3号选手 起跑!

第2组:2号选手 起跑!

第2组:1号选手 起跑!

第2组:3号选手 end!

第2组:2号选手 end!

第2组:1号选手 end!


这确实使我们想要的结果。

那么使用CyclicBarrier会产生什么样的结果呢???

试验一下:

import java.io.IOException;import java.util.Random;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CountDownLatch;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test6 {        public static void main(String[] args) throws IOException, InterruptedException {          //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去        CountDownLatch begin = new CountDownLatch(3);            ExecutorService executor = Executors.newFixedThreadPool(3);          executor.submit(new Thread(new Runner(begin, "第1棒:1号选手")));          executor.submit(new Thread(new Runner(begin, "第1棒:2号选手")));          executor.submit(new Thread(new Runner(begin, "第1棒:3号选手")));        executor.submit(new Thread(new Runner(begin, "第2棒:1号选手")));          executor.submit(new Thread(new Runner(begin, "第2棒:2号选手")));          executor.submit(new Thread(new Runner(begin, "第2棒:3号选手")));                 executor.shutdown();      }  }    class Runner implements Runnable {      // 当begin为0时,运动员起跑 private CountDownLatch begin;      private String name;        public Runner(CountDownLatch begin, String name) {          super();          this.begin = begin;          this.name = name;      }        @Override      public void run() {          try {              Thread.sleep(1000 * (new Random()).nextInt(8));              System.out.println(name + " 准备好了...");              //count减一,当count为0时,等待结束,所有运动员准备完毕,比赛开始              begin.countDown();            //等待所有运动员准备好            begin.await();          } catch (InterruptedException e) {              e.printStackTrace();          }         System.out.println(name + " 起跑!");          try {Thread.sleep((long)(Math.random()*1000));} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}         System.out.println(name + " end!");      }  }  

输出结果:

第1组:2号选手 准备好了...

第1组:1号选手 准备好了...

第1组:3号选手 准备好了...

第1组:3号选手 起跑!

第1组:2号选手 起跑!

第1组:1号选手 起跑!

第1组:1号选手 end!

第1组:3号选手 end!

第1组:2号选手 end!

第2组:3号选手 准备好了...

第2组:3号选手 起跑!

第2组:3号选手 end!

第2组:1号选手 准备好了...

第2组:1号选手 起跑!

第2组:1号选手 end!

第2组:2号选手 准备好了...

第2组:2号选手 起跑!

第2组:2号选手 end!


因为count归零后一直保持为0,不可复用,所以使用一个CountDownLatch无法实现上述功能。

有没有发现CyclicBarrier这个名字真是起绝了,一个cycle....一切尽在不言中.....





0 0
原创粉丝点击