Java的几个同步辅助类
Java为我们提供了一些同步辅助类
,利用这些辅助类我们可以在多线程编程中,灵活地把握线程的状态。
CountDownLatch
CountDownLatch
一个同步辅助类
,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
再CountDownLatch中两个比较关键的方法:
public void await() throws InterruptedException;
public void countDown();
CountDownLatch
是一个计数器
,它的构造方法中需要设置一个数值,用来设定计数的次数。每次调用countDown()
方法之后,这个计数器都会减去1
,CountDownLatch会一直阻塞着调用await()
方法的线程,直到计数器
的值变为0
。
设想有这样一个功能需要Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给主线程去做汇总,利用CountDownLatch来完成就非常轻松。
public class CountDownLatchTest {
private static CountDownLatch count = new CountDownLatch(4);
private static ExecutorService service = Executors.newFixedThreadPool(6);
public static void main(String args[]) throws InterruptedException {
for (int i = 0; i < 4; i++) {
service.execute(() -> {
// 模拟任务耗时
try {
int timer = new Random().nextInt(5);
TimeUnit.SECONDS.sleep(timer);
System.out.printf("%s时完成磁盘的统计任务,耗费%d秒.\n", new Date().toString(), timer);
// 任务完成之后,计数器减一
count.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 主线程一直被阻塞,知道count的计数器被设置为0
count.await();
System.out.printf("%s时全部任务都完成,执行合并计算.\n", new Date().toString());
service.shutdown();
}
}
CyclicBarrier
Barrier
在英语中是屏障的意思,这个同步工具会阻塞调用的线程,直到条件满足时,阻塞的线程同时被打开。
public int await() throws InterruptedException, BrokenBarrierException
CyclicBarrier
初始化的时候,设置一个屏障数。线程调用await()
方法的时候,这个线程就会被阻塞,当调用await()
的线程数量到达屏障数的时候,主线程就会取消所有被阻塞线程的状态。
在CyclicBarrier
的构造方法中,还可以设置一个barrierAction
。
在所有的屏障都到达之后,会启动一个线程来运行这里面的代码。这里举一个例子:百米赛跑的运动员起跑前需要准备,所有选手准备完毕之后,才可以同时起跑。
public class CyclicBarrierTest {
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(8);
private static ExecutorService service = Executors.newFixedThreadPool(50);
public static void main(String args[]) {
for (int i = 1; i < 9; i++) {
service.execute(new Thread(new Runner(i, cyclicBarrier)));
}
service.shutdown();
}
}
// 运动员类
public class Runner implements Runnable {
private int number;
private CyclicBarrier cyclicBarrier;
public Runner(int number, CyclicBarrier cyclicBarrier) {
this.number = number;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
int timer = new Random().nextInt(5);
TimeUnit.SECONDS.sleep(timer);
System.out.printf("%d号选手准备完毕,准备时间%d\n", number, timer);
cyclicBarrier.await();
System.out.printf("%d号选手于%s时起跑!\n", number, new Date().toString());
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
输出:
1号选手准备完毕,准备时间0
4号选手准备完毕,准备时间0
5号选手准备完毕,准备时间1
8号选手准备完毕,准备时间1
3号选手准备完毕,准备时间2
2号选手准备完毕,准备时间3
7号选手准备完毕,准备时间3
6号选手准备完毕,准备时间3
7号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
2号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
5号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
6号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
3号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
8号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
4号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
1号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
相比CountDownLatch
,CyclicBarrier
是可以被循环使用的,而且遇到线程中断等情况时,还可以利用reset()
方法,重置计数器,从这些方面来说,CyclicBarrier
会比CountDownLatch
更加灵活一些。
Semaphore
Semaphore
被用于控制特定资源在同一个时间被访问的个数。类似连接池的概念,保证资源可以被合理的使用。
Semaphore
的几个重要方法:
// 获取资源
public void acquire() throws InterruptedException
// 释放资源
public void release()
Semaphore
的构造方法可以设置一个int值
来设置一个计数器,用于表示资源同时可以被多少外部环境使用。每使用一次acquire()
,计数器都会去减去一,而每次调用release()
计数器则会增加一。当计数器的值为0的时候,外部的环境被阻塞,直到Semaphore
有空闲的资源可以被使用。
public class SemaphoreTest {
private static Semaphore semaphore = new Semaphore(3);
private static ExecutorService service = Executors.newFixedThreadPool(6);
public static void main(String args[]) {
// 执行9个任务
for (int i = 0; i < 9; i++) {
service.execute(() -> {
try {
semaphore.acquire();
System.out.printf("%s时获取资源,并调用.\n", new Date().toString());
// 线程挂起3秒
TimeUnit.SECONDS.sleep(3);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
service.shutdown();
}
}
运行的结果就是:
Sun Mar 27 20:18:16 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:16 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:16 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:19 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:19 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:19 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:22 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:22 CST 2016时获取资源,并调用.
Sun Mar 27 20:18:22 CST 2016时获取资源,并调用.
虽然线程池允许6个最大线程数量,但是同一个时间内只用三个任务被执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?