JUC 常见三大辅助类
JUC 常见三大辅助类
CountDownLatch(减少计数)
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
用给定的计数 初始化 CountDownLatch
,由于调用了 countDown()
方法,所以在当前计数到达零之前,await
方法会一直受阻塞;之后,会释放所有等待的线程,await
的所有后续调用都将立即返回;这种现象只出现一次——计数无法被重置,如果需要重置计数,请考虑使用 CyclicBarrier
CountDownLatch
是一个通用同步工具,它有很多用途:将计数 1 初始化的 CountDownLatch
用作一个简单的开/关锁存器,或入口,在通过调用 countDown()
的线程打开入口前,所有调用 await
的线程都一直在入口处等待;用 N 初始化的 CountDownLatch
可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待
CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行减 1 的操作,计数器等于 0之前调用 await 方法线程处于等待状态直到计数器等于0
总结:
- CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,这些线程会阻塞
- 其它线程调用 countDown 方法会将计数器减 1(调用 countDown 方法的线程不会阻塞)
- 当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行
代码实战
场景:教室有7 个同学,当所有同学陆续离开教室后值班同学才可以关门
package com.yl.assist;
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch使用案例
*
* @author Y-wee
*/
public class CountDownLatchTest {
public static void main(String[] args) {
// 计数器(设置教室剩余学生数量)
CountDownLatch count = new CountDownLatch(7);
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
// 计数器减一(学生离开,教室剩余学生数量减一)
count.countDown();
System.out.println(Thread.currentThread().getName() + "离开了教室");
}, "student" + i).start();
}
try {
// 计数器等于0(教室剩余学生数量为0)之前main线程等待
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 计数器等于0,main线程被唤醒(教室剩余学生数量为0-教室关门)
System.out.println("教室关门");
}
}
执行结果
student1离开了教室
student2离开了教室
student3离开了教室
student4离开了教室
student5离开了教室
student6离开了教室
student7离开了教室
教室关门
CyclicBarrier(循环栅栏)
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用,因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier
CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后的语句,可以将 CyclicBarrier 理解为加 1 操作
代码实战
场景:集齐 7 颗龙珠就可以召唤神龙
package com.yl.assist;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* CyclicBarrierTest使用案例
*
* @author Y-wee
*/
public class CyclicBarrierTest {
private final static int NUMBER = 7;
public static void main(String[] args) {
/*
创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动
并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行
(集齐七颗龙珠才可以召唤神龙)
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()-> System.out.println("集齐七颗龙珠,召唤神龙成功"));
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
// 模拟收集龙珠
System.out.println(Thread.currentThread().getName()+"集到了");
try {
// 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待(龙珠集齐之前处于等待状态)
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "龙珠" + i).start();
}
}
}
执行结果
龙珠1集到了
龙珠2集到了
龙珠3集到了
龙珠4集到了
龙珠5集到了
龙珠6集到了
龙珠7集到了
集齐七颗龙珠,召唤神龙成功
Semaphore(信号灯)
一个计数信号量,从概念上讲,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个 acquire()
,然后再获取该许可,每个 release()
释放一个许可,从而可能释放一个正在阻塞的获取者;Semaphore 只对可用许可的号码进行计数,并采取相应的行动
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目
总结:Semaphore维护了一个许可集,当线程未获取到许可时处于等待状态,直到线程获取到许可
代码实战
场景:抢车位,7 部汽车 3 个停车位
package com.yl.assist;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Semaphore使用案例
*
* @author Y-wee
*/
public class SemaphoreTest {
public static void main(String[] args) {
// 设置信号量(许可集),模拟三个停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
try {
// 获取信号量(停车许可)
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "进入了停车位");
// 线程等待(停车3s)
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/*
注意:离开停车位的输出语句需要写在释放信号量之前,不然可能造成信号量(停车许可释放了),
但是车子还没有离开,此时,另一个线程(车子)获取到了停车许可进来了
*/
System.out.println(Thread.currentThread().getName() + "离开了停车位");
// 释放信号量(停车许可)
semaphore.release();
}
}, "车辆" + i).start();
}
}
}
执行结果
车辆1进入了停车位
车辆3进入了停车位
车辆4进入了停车位
车辆3离开了停车位
车辆1离开了停车位
车辆5进入了停车位
车辆6进入了停车位
车辆4离开了停车位
车辆7进入了停车位
车辆5离开了停车位
车辆6离开了停车位
车辆2进入了停车位
车辆7离开了停车位
车辆2离开了停车位
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?