CyclicBarrier和CountDownLatch 都位于java.util.concurrent 这个包下:
CountDownLatch | CyclicBarrier |
减计数方式 | 减计数方式 |
计算为0时释放所有等待的线程 | 计算为0时释放所有等待的线程 |
计数为0时,无法重置 | 计数为0时,计数可重置为总数 |
调用countDown方式计数减1,调用countDown的线程不会阻塞(调用await的线程阻塞,等待计数为0) | 调用await方式计数减1,同时阻塞该线程,等待计数到达0。 |
不可重复利用 | 可重复利用 |
一、CountDownLatch用法
CountDownLatch用于一个或者多个线程需要等待其它多个线程都完成之后才能继续执行。我们举个场景,比如公司搞团建,租了个大巴车,大巴车的司机如果想启动出发去目的地,必须要等到所有员工都陆陆续续上车了之后才可以开车出发。至于员工上车后,互不干涉,可以各自做各自的事情。接下来我们先介绍CountDownLatch的方法,再通过CountDownLatch代码实现这个场景。
1、CountDownLatch类只提供了一个构造器:
//参数count为计数值,表示阻塞的线程执行前,必须调用countDown次数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
2、await()方法,调用的await()方法的线程会被挂起,直到等待count值为0时才能继续执行
3、await(long timeout, TimeUnit unit)方法,和await类似,只是多了个等待超时时间,等待到指定的时间之后,即使count值没有为0,也会继续执行。
4、countDown方法,用于count计数减1。
5、场景代码实现:公司搞团建,租了个大巴车,大巴车的司机如果想启动出发去目的地,必须要等到所有员工都陆陆续续上车了之后才可以开车出发。至于员工上车后,互不干涉,可以各自做各自的事情。
代码示例:
代码执行结果如下:
二、CyclicBarrier用法
CyclicBarrier用于一系列多个线程,批次互相牵制,需要所有线程都达到某个共同的临界点后,所有线程才能取消阻塞,同时继续执行各自接下来的代码逻辑。我们还是以团建为例,进行举例。大巴司机把大家送到目的地后,大家都下车了,由于下车点与拓展基地入口还有点距离,大家步行前往,大家有的走得快,有点走得慢。由于公司是以集体申报团建活动的,基地入口需要明确所有人数都已经到达,只能一次性一起进入基地。早到达基地入口的员工,需要等待其它员工全部都到达才可入基地,少一个都不行哦!接下来我们先介绍CyclicBarrier的方法,再通过CyclicBarrier代码实现这个场景。
1、CyclicBarrier提供了两个构造器:参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
2、await()方法,用于挂起当前线程,同时进行减1操作【图9源码红色标注】,直到所有线程到达barrier的parties再同时执行后续任务。
3、await(long timeout,TimeUnit unit)方法,用于挂起当前线程,同时进行减1操作,直到所有线程到达barrier的parties再同时执行后续任务,或者等待时间超过给定的时间,也可继续执行后续任务。
4、reset方法,重置barrier回到初始化状态,即count重置为构造器传入parties。可重复利用。
5、场景代码实现:大巴司机把大家送到目的地后,大家都下车了,由于下车点与拓展基地入口还有点距离,大家步行前往,大家有的走得快,有点走得慢。由于公司是以集体申报团建活动的,基地入口需要明确所有人数都已经到达,只能一次性一起进入基地。早到达基地入口的员工,需要等待其它员工全部都到达才可入基地,少一个都不行哦!
代码示例:
代码执行结果如下:
CountDownLatch代码示例源码:
package org.andy.effective.java.study;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDriverTest {
// 列举几个员工上车后做的事情,便于理解调用countDown方法的线程是不阻塞的
private static List<String> staffDoingList = Arrays.asList("看电影", "听音乐", "打游戏", "聊天");
public static void main(String[] args) throws InterruptedException {
// 公司搞团建有20名员工
CountDownLatch countDownLatch = new CountDownLatch(20);
// 员工上车流程
for (int i = 1; i <= 20; i++) {
final int index = i;
new Thread(new Runnable() {
@Override
public void run() {
// 模拟员工上车之前该自己私事
Random random = new Random();
try {
Thread.sleep(1000 * random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
// 员工上车--司机开车之前必须完成
System.out.println(index + "号员工上车了");
countDownLatch.countDown();
// 员工上车之后做的事情--与司机开车没有关系【模拟员工上车之前该自己私事---调用休眠,便于解释countDown不阻塞】
try {
Thread.sleep(1000 * random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
// 随机员工做的事情
String doing = staffDoingList.get(new Random().nextInt(staffDoingList.size() - 1));
System.out.println(index + "号员工上车之后,在车上:" + doing);
}
}).start();
}
System.out.println("大巴司机等待员工上车.........");
countDownLatch.await();
System.out.println("所有员工都已经上车,司机可以启动出发了.......");
}
}
CyclicBarrier代码示例源码:
package org.andy.effective.java.study;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
// 列举几个员工进入拓展基地后做的事情,便于理解调用await方法的线程阻塞
private static List<String> staffDoingList = Arrays.asList("射击", "攀岩", "划船");
public static void main(String[] args) {
// 公司搞团建有20名员工
CyclicBarrier cyclicBarrier = new CyclicBarrier(20);
// 员工如基地流程
for (int i = 1; i <= 20; i++) {
final int index = i;
new Thread(new Runnable() {
@Override
public void run() {
// 模拟员工从下车走到拓展基地入口,有快、有慢
Random random = new Random();
try {
Thread.sleep(1000 * random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
// 员工到达基地入口
System.out.println(index + "号员工到达拓展基地入口,等待其他员工到达");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
// 随机员工做的事情
String doing = staffDoingList.get(new Random().nextInt(staffDoingList.size() - 1));
// 员工都到达入口,登记之后,陆续进入基地
System.out.println(index + "号员工进入了拓展基地,准备去:" + doing);
}
}).start();
}
}
}