简介:
CyclicBarrier类是一种允许一组线程互相等待以到达共同的障碍点的同步辅助工具类。CyclicBarrier在涉及一定大小的线程的程序中很有用,这些线程有时必须彼此等待(如一个主流程中需要多个前置条件满足时才可执行)。CyclicBarrier在释放等待线程后可以重用,所以称它为循环的barrier。
使用说明
使用场景:执行一个任务前明确需要N个前置条件。
例子:大家玩LOL(或王者农药)的时候在选定英雄进入游戏时,都会有一个段加载过程,这个过程中,加载快慢跟大家的电脑和网速有关(这个过程是并行的),等到大家都加载完毕(或者超时,即游戏开始条件为:需要等待10个玩家都加载完毕,一起进入)大家统一进入游戏。
闲话少说,看代码说话:
下面这块代码在没有使用的CyclicBarrier的时候,执行结果如下:
public class MyThread implements Runnable {
private int num;
public MyThread(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "玩家[" + num + "]连接成功,资源中ing");
try {
Thread.sleep((int) (Math.random() * 10000 + 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "玩家[" + num + "]加载完毕,进入游戏!,当前时间:" + System.currentTimeMillis());
}
}
/**
* @Description:测试类
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new MyThread(i));
thread.start();
}
}
}
Thread-0玩家[0]连接成功,资源中ing
Thread-4玩家[4]连接成功,资源中ing
Thread-3玩家[3]连接成功,资源中ing
Thread-1玩家[1]连接成功,资源中ing
Thread-2玩家[2]连接成功,资源中ing
Thread-1玩家[1]加载完毕,进入游戏!,当前时间:1542955868994
Thread-3玩家[3]加载完毕,进入游戏!,当前时间:1542955872150
Thread-4玩家[4]加载完毕,进入游戏!,当前时间:1542955874346
Thread-0玩家[0]加载完毕,进入游戏!,当前时间:1542955875400
Thread-2玩家[2]加载完毕,进入游戏!,当前时间:1542955878612
在没有使用CyclicBarrier时,玩家进入游戏时间是随机的,并不是等待最后一个加载完毕一并进入。
下面改造代码,使用CyclicBarrier:
public class MyThread implements Runnable {
private static final CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier(5);
private int num;
public MyThread(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "玩家[" + num + "]连接成功,资源中ing");
try {
Thread.sleep((int) (Math.random() * 10000 + 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "玩家[" + num + "]加载完毕,等待其他玩家ing...当前时间:"+System.currentTimeMillis());
/**
* 等待Barrier打破
*/
try {
CYCLIC_BARRIER.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "玩家[" + num + "]加载完毕,进入游戏!,当前时间:" + System.currentTimeMillis());
}
}
运行结果:
Thread-0玩家[0]连接成功,资源中ing
Thread-4玩家[4]连接成功,资源中ing
Thread-1玩家[1]连接成功,资源中ing
Thread-3玩家[3]连接成功,资源中ing
Thread-2玩家[2]连接成功,资源中ing
Thread-4玩家[4]加载完毕,等待其他玩家ing...当前时间:1542958869502
Thread-0玩家[0]加载完毕,等待其他玩家ing...当前时间:1542958872473
Thread-3玩家[3]加载完毕,等待其他玩家ing...当前时间:1542958872917
Thread-1玩家[1]加载完毕,等待其他玩家ing...当前时间:1542958873494
Thread-2玩家[2]加载完毕,等待其他玩家ing...当前时间:1542958873668
Thread-2玩家[2]加载完毕,进入游戏!,当前时间:1542958873668
Thread-4玩家[4]加载完毕,进入游戏!,当前时间:1542958873668
Thread-0玩家[0]加载完毕,进入游戏!,当前时间:1542958873668
Thread-3玩家[3]加载完毕,进入游戏!,当前时间:1542958873668
Thread-1玩家[1]加载完毕,进入游戏!,当前时间:1542958873668
这里可以看到玩家是有相互等待,最终一起开始(根据电脑性能情况,最后进入时间的打印
可能会有几毫秒的差别)。
个人理解
public CyclicBarrier(int parties) {
this(parties, null);
}
此构造方法中的parties 便是临界点(也是重置点),即当等待的线程数达到此值时便执行
CyclicBarrier.await()下面的代码逻辑,同时也将屏障重新开启,等待下一组线程,所
以也是可Cyclic的!
await的结束条件必须是:
一、达到临界值(当前等待执行线程数等于parties) ,如过是调用await()方法有2种情况:
1.如果是小于临界值则会一直等待,
2.如果是大于临界值会执行 currentThreadNum/parties 次,
且有currentThreadNum%parties个线程处于等待状态
二、超时调用 await(long timeout, TimeUnit unit)方法,
此方法在等待时间到了便继续这些下面代码并抛出异常,此异常不打断线程后续执行。