1. 定义
CountDownLatch提供了一个栅栏,在这个栅栏这里有n个线程等待另外m个线程完成任务的一个组件。
CyclicBarrier提供了一个屏障,保证n个线程全部到达这个屏障之后,再继续执行后续的代码。
2. 对比
- 二者语义有所不同,CountDownLatch提供的n个线程等待m个线程的组件,而CyclicBarrier提供的是一个对n个线程进行步调调整的组件。
- CountDownLatch使用更加简单,灵活,可以在一个线程中多次countDown,CyclicBarrier不支持。
- CountDownLatch的计数器不支持重置,CyclicBarrier支持。
- CyclicBarrier提供barrierAction,在线程全部到达后,立即执行这一任务,CountDownLatch不支持
3. 应用场景
CountDownLatch常用于主线程等待多个子线程执行任务完成,然后进行下一步操作。
CyclicBarrier可以提供一种简易的,轻量的类MapReduce操作,可以手动分配(map)任务给线程池,然后用barrierAction(reduce)来处理多个线程的执行结果。
4. Demo
CountDownLatch:
package org.lyh;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class CountDownLatchDemo {
private static CountDownLatch c = new CountDownLatch(3);
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(2);
/**
* countDown可以在任何地方调用,比如在一个线程中调用两次,保证自己的逻辑是正确的即可
*/
es.execute(new Thread(()->{
System.out.println(1);
c.countDown();
try {
TimeUnit.MILLISECONDS.sleep(2000); //do something
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(2);
c.countDown();
}));
es.execute(new Thread(()->{
System.out.println(3);
c.countDown();
}));
/**
* 主线程和一个子线程都在等在CountDownLatch计数达到0
*/
es.execute(new Thread(()->{
try {
c.await();
System.out.println("子线程等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
c.await(1000, TimeUnit.MILLISECONDS);//设置最长等待时间
es.shutdown();
System.out.println("主线程等待结束");
}
}
CyclicBarrier:
package org.lyh;
import java.util.concurrent.*;
public class CyclicBarrierDemo implements Runnable{
/**
* CyclicBarrier设置最多等待的任务数量,以及全部任务结束以后立即执行的任务(非必选)
*/
private static CyclicBarrier c = new CyclicBarrier(3, new CyclicBarrierDemo());
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
/**
* await在一个线程里只能调用一次,不能多次调用,CyclicBarrier也不存在多个线程专门等待n个线程的问题
* 而是让这n个线程全部阻塞在await调用的地方,仔细想想和CountDownLatch还是有许多不同
* 而且CyclicBarrier支持重置计数器,可以支持更加复杂的业务场景
*/
es.execute(new Thread(()->{
try {
c.await();
TimeUnit.MILLISECONDS.sleep(2000); //do something
System.out.println(1);
} catch (Exception e) {
e.printStackTrace();
}
}));
es.execute(new Thread(()->{
System.out.println(3);
try {
c.await();
} catch (Exception e) {
e.printStackTrace();
}
}));
try {
System.out.println(2);
c.await();
} catch (Exception e) {
e.printStackTrace();
}
es.shutdown();
System.out.println("所有线程全部结束");
}
@Override
public void run() {
System.out.println("这是结束的工作");
}
}
CountDownLatch和CyclicBarrier分别是基于AQS和ReentrantLock实现的,具体原理需要的时候可以看源码。
参考文献:
《Java编程思想》
《Java并发编程的艺术》