Java程序有的时候在主线程中会创建多个线程去执行任务,然后在主线程执行完毕之前,把所有线程的任务进行汇总,以前可以用线程的join方法,但是这个方法不够灵活,我们可以使用CountDownLatch类,实现更优雅,而且使用线程池的话,可没有办法调用线程的join方法的呀!
一.简单使用CountDownLatch
直接使用线程:
package com.example.demo.study;
import java.util.concurrent.CountDownLatch;
public class Study0215 {
//这里相当于新建一个初始值为2的计数器
private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
try {
Thread.sleep(1000);
System.out.println("线程一执行完毕");
} catch (Exception e) {
}finally {
//每调用这个方法计数器减一
countDownLatch.countDown();
}
}).start();
new Thread(()->{
try {
Thread.sleep(1000);
System.out.println("线程二执行完毕");
} catch (Exception e) {
}finally {
countDownLatch.countDown();
}
}).start();
System.out.println("两个线程已经全部启动");
//只要调用了这个方法之后,主线程会阻塞,直到计数器countDownLatch变成0就会返回
countDownLatch.await();
System.out.println("执行完毕");
}
}
实际中尽量少直接操作线程,而是使用线程池:
package com.example.demo.study;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Study0215 {
// 这里相当于新建一个初始值为2的计数器
private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//将任务一丢进线程池
pool.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("线程一执行完毕");
} catch (Exception e) {
} finally {
// 每调用这个方法计数器减一
countDownLatch.countDown();
}
});
//任务二丢进线程池
pool.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("线程二执行完毕");
} catch (Exception e) {
} finally {
countDownLatch.countDown();
}
});
System.out.println("两个线程已经全部启动");
// 只要调用了这个方法之后,主线程会阻塞,直到计数器countDownLatch变成0就会返回
countDownLatch.await();
System.out.println("执行完毕");
}
}
二.await方法
看下面的图,可以知道这个CountDownLatch类内部有个工具类Sync实现了AQS,然后CountDownLatch中的方法都是调用工具类Sync去操作的,emmm....跟前面说过的ReentrantLock类结构是一样的;
我们看看CountDownLatch构造器传递的数其实就是设置AQS中state的值:
//实际上调用把值传递给了Sync,也就是设置了AQS中的state
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Sync(int count) {
setState(count);
}
我们再看看await方法:
//当前线程调用了await方法之后,当前线程就会给阻塞,直到以下两种情况:
//1.其他线程调用了countDown方法将计数器减到0之后,该线程就返回了;
//2.其他线程调用了当前的线程的中断方法,当前线程抛出异常InterruptedException
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
//当前线程被中断就抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//查看计数器中的值是不是0,不过不是0,就进入AQS等待队列等待;
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
三.countDown方法
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//tryReleaseShared方法返回false,说明当前计数器的值减一成功
//返回true,说明计数器的值此时为0,那就要唤醒因为调用了CountDownLatch而阻塞的线程
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
//一个无限循环
for (;;) {
//获取state的值
int c = getState();
//如果state为0,返回false
if (c == 0)
return false;
//否则就把state减一然后用CAS更新到state
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
四.getState方法
这个方法获取计数器的值,其实就是获取AQS中的state的值;
int getCount() {
return getState();
}
protected final int getState() {
return state;
}
其实CountDownLatch比较容易,功能和Thread的join方法一样,只不过更灵活,基于AQS实现,在初始化的时候设置state的值,当线程调用CountDownLatch的await方法的时候,当前线程就会被丢到AQS的阻塞队列挂起;然后当其他线程调用了countDown方法,其实就是将state减一,当state等于0的时候,就会唤醒所有因为调用await方法而阻塞的线程;
来源:https://www.cnblogs.com/wyq1995/p/12315072.html
标签:Java,线程同步器,CountDownLatch
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
Java擦除和转换实例分析
2022-03-30 02:19:45
C# TcpClient网络编程传输文件的示例
2021-10-16 16:01:13
![](https://img.aspxhome.com/file/2023/2/81062_0s.jpg)
Java多线程之同步工具类Exchanger
2022-07-05 03:50:54
Maven引入本地Jar包并打包进War包中的方法
2023-06-16 12:43:28
![](https://img.aspxhome.com/file/2023/6/62966_0s.png)
Springboot中登录后关于cookie和session拦截问题的案例分析
2022-09-25 19:26:44
![](https://img.aspxhome.com/file/2023/2/108932_0s.gif)
Java多态中动态绑定原理解析
2021-08-01 06:39:23
一文带你学会Java事件机制
2022-07-22 05:05:11
java多线程通过CompletableFuture组装异步计算单元
2023-07-19 10:15:42
![](https://img.aspxhome.com/file/2023/0/57620_0s.jpg)
SpringSecurity解决POST方式下CSRF问题
2023-07-18 18:59:51
Spring Bean初始化及销毁多种实现方式
2023-06-10 13:14:01
![](https://img.aspxhome.com/file/2023/0/71620_0s.jpg)
Java的内部类总结
2022-08-13 01:33:41
Unity实现新手引导镂空效果
2022-07-04 22:50:23
![](https://img.aspxhome.com/file/2023/9/125999_0s.jpg)
Java File类提供的方法与操作
2023-08-29 09:10:41
C#词法分析器之正则表达式的使用
2023-06-21 13:10:58
![](https://img.aspxhome.com/file/2023/2/66592_0s.png)
1小时快速上手RabbitMQ(简介及安装过程)
2022-04-26 15:49:40
![](https://img.aspxhome.com/file/2023/1/69331_0s.jpg)
java实现水果超市管理系统
2022-02-03 12:52:32
C#条件语句、循环语句(if、while)
2023-09-27 19:49:23
Java Lambda表达式入门示例
2022-03-19 00:15:47
java Mail邮件接收工具类
2022-04-24 14:17:17
Java自动生成趋势比对数据的方法分享
2023-11-25 18:29:18
![](https://img.aspxhome.com/file/2023/7/61087_0s.jpg)