java 多线程系列文章列表, 请查看目录: 《java 多线程学习笔记》
在企业开发中, 采用异步线程可以提升系统性能. 但是在异步线程开发中, 有时主线程需要监听异步线程何时执行结束, 然后再做一些其它操作. java 中有两种方式监听异步线程的结束: CountDownLatch 和 join.
1. CountDownLatch 方式
1.1 自定义线程类
- 自定义线程类需要添加一个CountDownLatch 计数器, 建议在构造方法赋值
- 务必保证CountDownLatch 自减操作在finally 代码快中
class MyThread extends Thread{
// 计数器
private CountDownLatch countDownLatch;
// 创建线程实例时, 初始化计数器
public MyThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
// 计数器自减操作必须放在finally中, 保证一定会执行
@Override
public void run() {
try {
Thread.sleep(2000l);
System.out.println(Thread.currentThread().getName() + "-执行..");
} catch (Exception ex) {
}finally {
// 放到finally块儿中, 确保一定会执行
countDownLatch.countDown();
}
}
}
1.2 测试用例
- CountDownLatch 的值务必保证和需要监控的多线程的数量一致
- countDownLatch.await() 会阻塞主线程, 等监听的异步线程均结束后, 继续进行
public static void main(String[] args) throws InterruptedException {
// 线程数量
int threadNum = 5;
// CountDownLatch 初始化值必须和线程数量一致
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 创建固定数量的线程并启动
for (int i = 0; i < threadNum; i++) {
new MyThread(countDownLatch).start();
}
System.out.println("执行主线程逻辑, 主线程开始等待多线程执行结束...");
// 主线程等待异步线程结束
countDownLatch.await();
System.out.println("异步线程全部执行结束, 继续执行主线程逻辑");
}
1.3 测试输出
执行主线程逻辑, 主线程开始等待多线程执行结束...
Thread-0-执行..
Thread-1-执行..
Thread-2-执行..
Thread-3-执行..
Thread-4-执行..
异步线程全部执行结束, 继续执行主线程逻辑
2. join 方式
- Thread 的提供了join 方法, 用于一个线程监听另一个线程结束的方法.
- join方法通常用于监听一个异步线程的情况, 不用于监听多个异步线程.
- join方式相较于CountDownLatch, 需要持有异步线程的引用. 耦合性较强.
2.1 join API
方法签名 | 方法描述 |
---|---|
public final void join() throws InterruptedException | 等待异步线程结束, 一直等待, 不超时 |
public final synchronized void join(long millis) | 等待时长为millis毫秒, 超时之后不再等待, 也不抛出异常 |
public final synchronized void join(long millis, int nanos) | 操作系统本身无法精确到纳秒, 所以不常使用 |
2.2 测试用例
public static void main(String[] args) throws InterruptedException {
// 1. 创建异步线程, 并启动
Thread thread = new Thread(){
@Override
public void run() {
try {
System.out.println("异步线程休眠第1秒.");
Thread.sleep(1000l);
System.out.println("异步线程休眠第2秒..");
Thread.sleep(1000l);
System.out.println("异步线程休眠第3秒..");
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
// 主线程休眠1秒后再执行, 先让异步线程执行1秒
Thread.sleep(1000l);
System.out.println("主线程执行业务逻辑, 开始等待异步线程执行结束..");
// 主线程开始等待
thread.join();
System.out.println("异步线程执行结束, 主线程继续执行业务逻辑");
}
2.3 测试输出
异步线程休眠第1秒.
主线程执行业务逻辑, 开始等待异步线程执行结束..
异步线程休眠第2秒..
异步线程休眠第3秒..
异步线程执行结束, 主线程继续执行业务逻辑