一 CyclicBarrier类的介绍
CyclicBarrier意思是循环屏障,是juc(java.util.concurrent)包下重要的类。在构造CyclicBarrier对象的时候需要传入一组线程的数量值,这组的任一线程到达屏障后就处于阻塞状态,直到这组线程都到达屏障,屏障才会开启,即这组所有的线程才会继续执行线程任务。又因为是循环屏障,所以另一组同样数量的线程也可以使用这个屏障,让这组线程相互等待,等到屏障开启时再继续执行线程任务。
二 CyclicBarrier类的方法
public CyclicBarrier(int parties):创建指定线程数量parties的CyclicBarrier对象,任一线程到达屏障都会阻塞,直到parties个线程到达屏障才会继续执行任务;
public CyclicBarrier(int parties, Runnable barrierAction):创建指定线程数量parties的CyclicBarrier对象,任一线程到达屏障都会阻塞,直到parties个线程到达屏障时,先执行barrierAction线程任务,然后所有的等待线程再继续执行任务;
public int await():线程执行CyclicBarrier对象的await()方法,表示当前线程已经到达屏障,线程处于阻塞状态;
public int await(long timeout, TimeUnit unit):线程执行CyclicBarrier对象的await(timeout)方法,表示当前线程已经到达屏障,线程处于超时阻塞状态:
如果在超时时间范围内,所有的线程都到达屏障,那么线程正常执行;
如果超时时间到了,仍然还有线程没有到达屏障,那么线程就会抛出java.util.concurrent.
TimeoutException超时异常,然后线程任务继续执行;
public void reset():将屏障重置为其初始状态;
public boolean isBroken():判断屏障是否损坏状态:
如果线程任务正常执行,屏障规则没有损坏,返回false;
如果线程任务抛出中断异常,或是超时异常,屏障规则损坏,返回true;
public int getParties():返回开启屏障所需要的线程数量,也即是创建CyclicBarrier对象设置的值;
public int getNumberWaiting():返回正在屏障前等待的线程数量;
三 CyclicBarrier类的使用
3.1 CyclicBarrier类的基本使用
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()
+ "开始执行任务....");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()
+ "完成执行任务....");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "线程001").start();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()
+ "开始执行任务....");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()
+ "完成执行任务....");
TimeUnit.SECONDS.sleep(5);
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()
+ "重新完成执行任务....");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "线程002").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(6);
System.out.println(Thread.currentThread().getName()
+ "开始执行任务....");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()
+ "完成执行任务....");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "线程003").start();
// 线程001开始执行任务....
// 线程002开始执行任务....
// 线程002完成执行任务....
// 线程001完成执行任务....
// 线程003开始执行任务....
// 线程003完成执行任务....
// 线程002重新完成执行任务....
}
创建一个parties为2的CyclicBarrier对象:
线程001先到达屏障,处于阻塞状态;
线程002接着到达屏障,和线程001一起满足屏障开启的条件,线程002和线程001接着继续执行代码;
线程002再次执行CyclicBarrier对象的await()方法,会再次处于阻塞状态,此时是重新使用屏障,在一个线程任务里面可以多次执行CyclicBarrier对象的await()方法;
线程003接着到达屏障,和线程002一起满足屏障开启的条件,线程003和线程002接着继续执行代码;
3.2 CyclicBarrier类的超时等待
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
new Thread(() -> {
System.out.println(Thread.currentThread().getName()
+ "开始执行线程任务, 当前时间是 "
+ LocalDateTime.now().format(formatter));
try {
cyclicBarrier.await(3, TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName()
+ "超时状态下, try里面的代码不会执行, 当前时间是 "
+ LocalDateTime.now().format(formatter));
} catch (InterruptedException | BrokenBarrierException
| TimeoutException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "超时状态下, try外面的代码会执行, 当前时间是 "
+ LocalDateTime.now().format(formatter));
}, "线程001").start();
// 线程001开始执行线程任务, 当前时间是 2024-04-18 00:44:02
// java.util.concurrent.TimeoutException
// 线程001超时状态下, try外面的代码会执行, 当前时间是 2024-04-18 00:44:05
}
线程001执行超时阻塞方法,在超时时间范围内屏障没有开启,线程001抛出TimeoutException异常,而且try代码块里面的代码不再执行,接着try代码块后面的代码执行
3.3 CyclicBarrier类的barrierAction任务
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有的线程都已经到达, 请所有线程继续执行...");
});
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
new Thread(() -> {
System.out.println(Thread.currentThread().getName()
+ "开始执行线程任务, 当前时间是 "
+ LocalDateTime.now().format(formatter));
try {
cyclicBarrier.await(3, TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName()
+ "正常状态下, try里面的代码会执行, 当前时间是 "
+ LocalDateTime.now().format(formatter));
} catch (InterruptedException | BrokenBarrierException
| TimeoutException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正常状态下, try外面的代码会执行, 当前时间是 "
+ LocalDateTime.now().format(formatter));
}, "线程001").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()
+ "开始执行线程任务, 当前时间是 "
+ LocalDateTime.now().format(formatter));
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "执行线程任务完成, 当前时间是 "
+ LocalDateTime.now().format(formatter));
}, "线程002").start();
// 线程001开始执行线程任务, 当前时间是 2024-04-18 00:57:37
// 线程002开始执行线程任务, 当前时间是 2024-04-18 00:57:37
// 所有的线程都已经到达, 请所有线程继续执行...
// 线程001正常状态下, try里面的代码会执行, 当前时间是 2024-04-18 00:57:42
// 线程002执行线程任务完成, 当前时间是 2024-04-18 00:57:42
// 线程001正常状态下, try外面的代码会执行, 当前时间是 2024-04-18 00:57:42
}
创建一个parties为2的CyclicBarrier对象,线程001和线程002都执行CyclicBarrier对象的await()方法,即到达屏障的线程数量满足屏障开启的条件,先执行构造CyclicBarrier对象时的barrierAction任务,然后是到达屏障的所有线程接着执行线程任务