简单记录一下CyclicBarrier的使用
简介
- CyclicBarrier是一个同步辅助类,它允许一组线程相互等待直到所有线程都到达一个公共的屏障点。
- 在程序中有固定数量的线程,这些线程有时候必须等待彼此,这种情况下,使用CyclicBarrier很有帮助。
- 这个屏障之所以用循环修饰,是因为在所有的线程释放彼此之后,这个屏障是可以重新使用的。
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
就像长途汽车站提供长途客运服务。当等待坐车的乘客到达20人时,汽车站就会发出一辆长途汽车,让这20个乘客上车走人。等到下次等待的乘客又到达20人是,汽车站就会又发出一辆长途汽车。
CyclicBarrier和CountDownLatch有相似点,他们都是通过计数器来实现的,但是CyclicBarrier的技术器可以重置,多次使用,CountDownLatch是等待一个或者n个线程执行完成之后做一些事,CyclicBarrier是多个线程之间相互等待,CyclicBarrier中的每一个线程执行完成之后调用await()
方法(计数器加1
)进入等待,当所有线程都执行完成之后,才会继续往下执行。
下面看代码:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
private final static int threadNum = 10;
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool(); //创建一个线程池
for(int i = 0; i < threadNum; i++){
final int finalI = i;
Thread.sleep(1000); //每个线程执行前睡眠1秒
executorService.execute(() -> {
try {
test(finalI);
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
private static void test(int num) throws Exception{
Thread.sleep(1000);
System.out.println(num + " 当前线程执行一些操作... " + new SimpleDateFormat(" HH:mm:ss").format(new Date()));
cyclicBarrier.await(); //执行完成 之后调用await() 表示当前自己已经执行完成
System.out.println(num + " 5个都执行完了... " + new SimpleDateFormat(" HH:mm:ss").format(new Date()));// 如果要输出这段话 必须是5个都执行完成 才会执行这个
}
}
这段代码的执行结果是:
0 当前线程执行一些操作... 17:47:07
1 当前线程执行一些操作... 17:47:08
2 当前线程执行一些操作... 17:47:09
3 当前线程执行一些操作... 17:47:10
4 当前线程执行一些操作... 17:47:11
0 5个都执行完了... 17:47:11
3 5个都执行完了... 17:47:11
4 5个都执行完了... 17:47:11
2 5个都执行完了... 17:47:11
1 5个都执行完了... 17:47:11
5 当前线程执行一些操作... 17:47:12
6 当前线程执行一些操作... 17:47:13
7 当前线程执行一些操作... 17:47:14
8 当前线程执行一些操作... 17:47:15
9 当前线程执行一些操作... 17:47:16
9 5个都执行完了... 17:47:16
5 5个都执行完了... 17:47:16
6 5个都执行完了... 17:47:16
7 5个都执行完了... 17:47:16
8 5个都执行完了... 17:47:16
从上面这个可以看出来 当调用await()
的时候不足5个
, 那么不会执行await()
后面的代码 ,当达到5个
之后就会里面执行await()
后面的代码
cyclicBarrier还有一个用法就是当达到一个屏障点的时候,优先执行一段代码,然后再继续执行下面的,看例子:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
private final static int threadNum = 10;
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
System.out.println("达到5个之后先执行这里的代码");
});
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool(); //创建一个线程池
for(int i = 0; i < threadNum; i++){
final int finalI = i;
Thread.sleep(1000); //每个线程执行前睡眠1秒
executorService.execute(() -> {
try {
test(finalI);
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
private static void test(int num) throws Exception{
Thread.sleep(1000);
System.out.println(num + " 当前线程执行一些操作... " + new SimpleDateFormat(" HH:mm:ss").format(new Date()));
cyclicBarrier.await(); //执行完成 之后调用await() 表示当前自己已经执行完成
System.out.println(num + " 5个都执行完了... " + new SimpleDateFormat(" HH:mm:ss").format(new Date()));// 如果要输出这段话 必须是5个都执行完成 才会执行这个
}
}
这段代码的输出结果是:
0 当前线程执行一些操作... 17:58:15
1 当前线程执行一些操作... 17:58:16
2 当前线程执行一些操作... 17:58:17
3 当前线程执行一些操作... 17:58:18
4 当前线程执行一些操作... 17:58:19
达到5个之后先执行这里的代码
0 5个都执行完了... 17:58:19
1 5个都执行完了... 17:58:19
2 5个都执行完了... 17:58:19
3 5个都执行完了... 17:58:19
4 5个都执行完了... 17:58:19
5 当前线程执行一些操作... 17:58:20
6 当前线程执行一些操作... 17:58:21
7 当前线程执行一些操作... 17:58:22
8 当前线程执行一些操作... 17:58:23
9 当前线程执行一些操作... 17:58:24
达到5个之后先执行这里的代码
9 5个都执行完了... 17:58:24
6 5个都执行完了... 17:58:24
5 5个都执行完了... 17:58:24
8 5个都执行完了... 17:58:24
7 5个都执行完了... 17:58:24
从输出结果就可以看出来 每次达到屏障点就先执行一段代码再继续执行。
这就是简单的CyclicBarrier使用
个人浅薄理解,欢迎补充