在多线程开发中,我们总会有一些需求处理,需要在多个线程全部执行完毕后,最后执行的。例如:统计所有线程的运行时长。
一般直接使用多线程是无法统计的,我们先来演示一个无法统计的情况。
//定义计算偶数的线程类
class LatchDemo implements Runnable {
@Override
public void run() {
//遍历50000,当计算到为偶数,则打印
for (int i = 0; i < 50000; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
}
public class TestCountDownLatch {
public static void main(String[] args) {
//创建线程类对象
LatchDemo latchDemo = new LatchDemo();
//记录开始时间
long start = System.currentTimeMillis();
//创建5个线程执行
for (int i = 0; i < 5; i++) {
new Thread(latchDemo).start();
}
//记录结束时间
long end = System.currentTimeMillis();
System.out.println("耗时时间为: " + (end - start));
}
}
测试执行如下:
image-20201102074319348
可以从执行来看,并没有打印出 main 线程的统计时间,但是其实 main 线程已经执行了。
因为 main 线程是与 计算偶数的5个线程 同时并发执行了。但是,我们想要的效果是 计算偶数的5个线程之后, main 线程再次执行统计的时间耗时。
这时候就需要线程等待的操作,为了实现这个效果,下面我们来介绍一下 CountDownLatch 闭锁。
- Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能。
- CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
- 闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:
- 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
- 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
- 等待直到某个操作所有参与者都准备就绪再继续执行。
看完了基本介绍之后,我们使用 CountDownLatch 闭锁 来完成我们前面想要的效果。
image-20201102075156069
//定义计算偶数的线程类
class LatchDemo implements Runnable {
//定义闭锁
private CountDownLatch latch;
//通过构造器接收闭锁
public LatchDemo(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
try {
//遍历50000,当计算到为偶数,则打印
for (int i = 0; i < 50000; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}finally {
//递减闭关锁的count次数,当count=0,则结束闭关锁
latch.countDown();
}
}
}
image-20201102075519521
public class TestCountDownLatch {
public static void main(String[] args) {
//创建闭锁,在这里有一个count的线程次数 5
//count次数 5 说明:当执行完毕了5个线程,则允许继续往下执行,结束等待
final CountDownLatch latch = new CountDownLatch(5);
//创建线程类对象
LatchDemo latchDemo = new LatchDemo(latch); // 将闭锁传入线程对象
//记录开始时间
long start = System.currentTimeMillis();
//创建5个线程执行
for (int i = 0; i < 5; i++) {
new Thread(latchDemo).start();
}
//设置锁等待:等待count次数被线程递减countDown为0
//当count=0,则结束等待阻塞,继续下面的操作
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//记录结束时间
long end = System.currentTimeMillis();
System.out.println("耗时时间为: " + (end - start));
}
}
image-20201102075620228
通过闭关锁,我们可以阻塞 main 线程的执行,直到所有计算偶数的线程结束,最后计算出运行的耗时。