CyclicBarrier 的理解
CyclicBarrier 是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环的 barrier 。
举个例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier 。
CyclicBarrier 的方法说明
构造方法
CyclicBarrier(int parties):创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待它时,它将跳闸,并且当屏障跳闸时不执行预定义的动作。
CyclicBarrier(int parties, Runnable barrierAction):创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行。
方法
- int await() 等待所有 parties 已经在这个障碍上调用了 await 。
- int await(long timeout, TimeUnit unit) 等待所有 parties 已经在此屏障上调用 await ,或指定的等待时间过去。
- int getNumberWaiting() 返回目前在屏障处等待的线程个数。
- int getParties() 返回履行这个障碍所需的 parties 数量。
- boolean isBroken() 查询这个障碍是否处于破碎状态。
- void reset() 将屏障重置为初始状态。
CyclicBarrier 的用法
CyclicBarrier 核心主要有两点:线程组内彼此相互等待,然后大家开始做某件事;这一代结束后开始下一代–重用思想。
CountDownLatch 核心思想为确保某些活动直到其他活动都完成才继续执行,而且 CountDownLatch 不可重用。
举例,班级集体野炊,在大巴上等待所有同学到来才开始出发,一个班级集合完毕出发一辆大巴,这是 CyclicBarrier 。到达目的地后需要同学自行寻找食材,寻找到需要的所有食材后才开始做饭,一个班级做饭活动是一次性的,这是 CountDownLatch 。
典型用法1
赛跑时,等待所有人都准备好时,才起跑。
代码示例:
import lombok.extern.slf4j.Slf4j;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import static java.util.concurrent.Executors.*;
/**
* @author: Xuxu
* @date: 2020-08-31 10:48
**/
@Slf4j
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
ExecutorService executorService = newFixedThreadPool(3);
for (int i = 1; i <= 3; i++) {
executorService.submit(new Thread(new Runner(cyclicBarrier, i + "号选手")));
}
executorService.shutdown();
}
static class Runner implements Runnable {
/**
* 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
*/
private CyclicBarrier barrier;
private String name;
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 | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(name + " 起跑!");
}
}
}
运行结果:
1号选手 准备好了...
3号选手 准备好了...
2号选手 准备好了...
2号选手 起跑!
1号选手 起跑!
3号选手 起跑!
典型用法2
CyclicBarrier 等待与复用:宿舍四哥们约着去操场打球
代码示例:
import java.util.concurrent.*;
/**
* @author: Xuxu
* @date: 2020-08-31 11:31
**/
public class CyclicBarrierTest1 {
private static final ExecutorService executorService = Executors.newFixedThreadPool(4);
/**
* 当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
*/
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4,
() -> System.out.println("寝室四兄弟一起出发去球场")
);
private static class GoThread extends Thread {
private final String name;
GoThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + "开始从宿舍出发");
try {
cyclicBarrier.await();//拦截线程
System.out.println(name + "从楼底下出发");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] str = {"李明", "王强", "刘凯", "赵杰"};
String[] str1 = {"王二", "洪光", "雷兵", "赵三"};
for (int i = 0; i < 4; i++) {
executorService.execute(new GoThread(str[i]));
}
try {
Thread.sleep(4000);
System.out.println("四个人一起到达球场,现在开始打球");
System.out.println("现在对CyclicBarrier进行复用.....");
System.out.println("又来了一拨人,看看愿不愿意一起打:");
} catch (InterruptedException e) {
e.printStackTrace();
}
cyclicBarrier.reset();
//进行复用:
for (int i = 0; i < 4; i++) {
executorService.execute(new GoThread(str1[i]));
}
try {
Thread.sleep(4000);
System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
运行结果:
刘凯开始从宿舍出发
赵杰开始从宿舍出发
王强开始从宿舍出发
李明开始从宿舍出发
寝室四兄弟一起出发去球场
李明从楼底下出发
刘凯从楼底下出发
赵杰从楼底下出发
王强从楼底下出发
四个人一起到达球场,现在开始打球
现在对CyclicBarrier进行复用.....
又来了一拨人,看看愿不愿意一起打:
王二开始从宿舍出发
洪光开始从宿舍出发
雷兵开始从宿舍出发
赵三开始从宿舍出发
寝室四兄弟一起出发去球场
赵三从楼底下出发
王二从楼底下出发
洪光从楼底下出发
雷兵从楼底下出发
四个人一起到达球场,表示愿意一起打球,现在八个人开始打球
CountDownLatch 和 CyclicBarrier 的比较
- CountDownLatch 是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而 CyclicBarrier 则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。
- CountDownLatch 是减计数方式,而 CyclicBarrier 是加计数方式。
- CountDownLatch 计数为0无法重置,而 CyclicBarrier 计数达到初始值,则可以重置。
- CountDownLatch 不可以复用,而 CyclicBarrier 可以复用。
评论区