CountDownLatch、CyclicBarrier、Semaphore、Callable、Future  都位于java.util.concurrent包下,其中CountDownLatch、CyclicBarrier和Semaphore属于该包中的tools分支,Callable和Future属于该包中的executer分支。

一.CountDownLatch

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

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

1 public CountDownLatch(int count) {  };  //参数count为计数值

然后下面这3个方法是CountDownLatch类中最重要的方法:

1 public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
2 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
3 public void countDown() { };  //将count值减1

例子:假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个磁盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,实现代码如下:

 1 public class Main {
 2     private static CountDownLatch count = new CountDownLatch(4);
 3     private static ExecutorService service = Executors.newFixedThreadPool(6);
 4     public static void main(String args[]) throws InterruptedException {
 5         for (int i = 0; i < 4; i++) {
 6             int finalI = i;
 7             service.execute(() -> {
 8                 // 模拟任务耗时
 9                 try {
10                     int timer = new Random().nextInt(5);
11                     TimeUnit.SECONDS.sleep(timer);
12                     System.out.printf("%s时完成磁盘%d的统计任务,耗费%d秒.\n", new Date().toString(), finalI, timer);
13                     // 任务完成之后,计数器减一
14                     count.countDown();
15                 } catch (InterruptedException e) {
16                     e.printStackTrace();
17                 }
18             });
19         }
20         // 下面语句由主线程调用,故主线程一直被阻塞,直到count的计数器被设置为0
21         count.await();
22         System.out.printf("%s时全部任务都完成,执行合并计算.\n", new Date().toString());
23         service.shutdown();
24     }
25 }

运行结果为:

Fri Aug 19 08:59:44 CST 2016时完成磁盘1的统计任务,耗费0秒.
Fri Aug 19 08:59:45 CST 2016时完成磁盘2的统计任务,耗费1秒.
Fri Aug 19 08:59:48 CST 2016时完成磁盘0的统计任务,耗费4秒.
Fri Aug 19 08:59:48 CST 2016时完成磁盘3的统计任务,耗费4秒.
Fri Aug 19 08:59:48 CST 2016时全部任务都完成,执行合并计算.

  当问题已经分解为许多部分,每个线程都被分配一部分计算时,CountdownLatch 非常有用。在工作线程结束时,它们将减少计数,协调线程可以在闩锁处等待当前这一批计算结束,然后继续移至下一批计算。
  相反地,具有计数 1 的 CountdownLatch 类可以用作"启动大门",来立即启动一组线程;工作线程可以在闩锁处等待,协调线程减少计数,从而立即释放所有工作线程。下例使用两个 CountdownLatche。一个作为启动大门,一个在所有工作线程结束时释放线程。

二.CyclicBarrier用法

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

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

1 public CyclicBarrier(int parties, Runnable barrierAction) {
2 
3 }
4 
5 public CyclicBarrier(int parties) {
6 
7 }

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

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

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

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

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

  例子:假若有若干个线程都要进行写数据操作,并且只有所有线程都完成写数据操作之后,这些线程才能继续做后面的事情,实现代码如下:

 1 public class Test {
 2     public static void main(String[] args) {
 3         int N = 4;
 4         CyclicBarrier barrier  = new CyclicBarrier(N);
 5         for(int i=0;i<N;i++)
 6             new Writer(barrier).start();
 7     } 
 8     static class Writer extends Thread{
 9         private CyclicBarrier cyclicBarrier;
10         public Writer(CyclicBarrier cyclicBarrier) {
11             this.cyclicBarrier = cyclicBarrier;
12         }
13 
14         @Override
15         public void run() {
16             System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
17             try {
18                 Thread.sleep(5000);      //以睡眠来模拟写入数据操作
19                 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
20                 cyclicBarrier.await();
21             } catch (InterruptedException e) {
22                 e.printStackTrace();
23             }catch(BrokenBarrierException e){
24                 e.printStackTrace();
25             }
26             System.out.println("所有线程写入完毕,继续处理其他任务...");
27         }
28     }
29 }

执行结果:

线程Thread-0正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
View Code

相关文章: