Java 并发流程工具的实战探索
文章目录
-
- 写在文章开头
- CountDownLatch
-
- 详解CountDownLatch工作流程
- 模拟等待工作完成
- 模拟运动员赛跑
- 从源码角度分析CountDownLatch工作流程(补充aqs文章)
- Semaphore
-
- 详解Semaphore
- 详解Semaphore工作原理
- Semaphore使用注意事项
- Condition
-
- 详解Condition
- 基于条件对象完成生产者、消费者模式
- 详解CyclicBarrier
-
- CyclicBarrier 原理和使用示例
- CyclicBarrier 与CountDownLatch区别(重点)
- 参考文献
写在文章开头
CountDownLatch
详解CountDownLatch工作流程
笔者一般称CountDownLatch
为倒计时门闩,它主要用于需要某些条件下才能唤醒的需求场景,例如我们线程1必须等到线程2做完某些事,那么就可以设置一个CountDownLatch
并将数值设置为1,一旦线程2完成业务逻辑后,将数值修改为0,此时线程1就会被唤醒:
模拟等待工作完成
通过上述的描述可能有点抽象,我们直接通过几个例子演示一下,我们现在有这样一个需求,希望等待5个线程完成之后,打印输出一句工作完成:
对应的代码示例如下,可以看到我们创建了数值为5的CountDownLatch
,一旦线程池里的线程完成工作后就调用countDown
进行扣减,一旦数值变为0,主线程await就会放行,执行后续输出:
int workerSize = 5;
CountDownLatch workCount = new CountDownLatch(workerSize);
ExecutorService threadPool = Executors.newFixedThreadPool(workerSize);
for (int i = 0; i < workerSize; i++) {
final int workerNum = i;
//5个工人输出完成工作后,扣减倒计时门闩数
threadPool.submit(() -> {
log.info("worker[{}]完成手头的工作", workerNum);
workCount.countDown();
});
}
try {
//阻塞当前线程(主线程)往后走,只有倒计时门闩变为0之后才能继续后续逻辑
log.info("等待worker工作完成");
workCount.await();
} catch (InterruptedException e) {
log.info("倒计时门闩阻塞失败,失败原因[{}]", e.getMessage(), e);
}
threadPool.shutdown();
while (!threadPool.isTerminated()) {
}
log.info("所有工人都完成手头的工作了");
对应的我们也给出输出结果,可以看到主线程在线程池线程完成后才输出:
模拟运动员赛跑
实际上CountDownLatch
可以让多个线程进行等待,我们不妨用线程模拟一下所有运动员就绪后,等待枪响后起跑的场景:
代码如下,每当运动员即线程池的线程准备就绪,则调用await等待枪响,一旦所有运动员就绪之后,主线程调用countDown
模拟枪响,然后运动员起跑:
public static void main(String[] args) {
log.info("百米跑比赛开始");
int playerNum = 3;
CountDownLatch gun = new CountDownLatch(1);
ExecutorService threadPool = Executors.newFixedThreadPool(playerNum);
for (int i = 0; i < playerNum; i++) {
final int playNo = i;
threadPool.submit(() -> {
log.info("[{}]号运动员已就绪", playNo);
try {
gun.await();
} catch (InterruptedException e) {
log.info("[{}]号运动员线程阻塞失败,失败原因[{}]", playNo, e.getMessage(), e);
}
log.info("[{}]号运动员已经到达重点", playNo);
});
}
//按下枪 所有运动员起跑
gun.countDown();
threadPool.shutdown();
while