CyclicBarrier
概念
和CountDownLatch相反,需要集齐七颗龙珠,召唤神龙。也就是做加法,开始是0,加到某个值的时候就执行
CyclicBarrier的字面意思就是可循环(cyclic)使用的屏障(Barrier)。它要求做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await方法
案例
集齐7个龙珠,召唤神龙的Demo,我们需要首先创建CyclicBarrier
/**
* 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
然后同时编写七个线程,进行龙珠收集,但一个线程收集到了的时候,我们需要让他执行await方法,等待到7个线程全部执行完毕后,我们就执行原来定义好的方法
for (int i = 0; i < 7; i++) {
final Integer tempInt = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 收集到 第" + tempInt + "颗龙珠");
try {
// 先到的被阻塞,等全部线程完成后,才能执行方法
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
完整代码
/**
* CyclicBarrier循环屏障
*
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
/**
* 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 0; i < 7; i++) {
final Integer tempInt = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 收集到 第" + tempInt + "颗龙珠");
try {
// 先到的被阻塞,等全部线程完成后,才能执行方法
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
CountDownLatch
概念
让一些线程阻塞直到另一些线程完成一系列操作才被唤醒
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程就会被阻塞。其它线程调用CountDown方法会将计数器减1(调用CountDown方法的线程不会被阻塞),当计数器的值变成零时,因调用await方法被阻塞的线程会被唤醒,继续执行
场景
现在有这样一个场景,假设一个自习室里有7个人,其中有一个是班长,班长的主要职责就是在其它6个同学走了后,关灯,锁教室门,然后走人,因此班长是需要最后一个走的,那么有什么方法能够控制班长这个线程是最后一个执行,而其它线程是随机执行的
解决方案
这个时候就用到了CountDownLatch,计数器了。我们一共创建6个线程,然后计数器的值也设置成6
// 计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
然后每次学生线程执行完,就让计数器的值减1
for (int i = 0; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 上完自习,离开教室");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
最后我们需要通过CountDownLatch的await方法来控制班长主线程的执行,这里 countDownLatch.await()可以想成是一道墙,只有当计数器的值为0的时候,墙才会消失,主线程才能继续往下执行
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t 班长最后关门");
不加CountDownLatch的执行结果,我们发现main线程提前已经执行完成了
1 上完自习,离开教室
0 上完自习,离开教室
main 班长最后关门
2 上完自习,离开教室
3 上完自习,离开教室
4 上完自习,离开教室
5 上完自习,离开教室
6 上完自习,离开教室
引入CountDownLatch后的执行结果,我们能够控制住main方法的执行,这样能够保证前提任务的执行
0 上完自习,离开教室
2 上完自习,离开教室
4 上完自习,离开教室
1 上完自习,离开教室
5 上完自习,离开教室
6 上完自习,离开教室
3 上完自习,离开教室
main 班长最后关门
完整代码
package com.moxi.interview.study.thread;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 上完自习,离开教室");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t 班长最后关门");
}
}