CountDownLatch 的介绍和使用
大家好,我是欧阳方超,微信公众号同名。
1 CountDownLatch简介
CountDownLatch是Java并发包中的一个同步工具类,始于JDK1.5,主要用于线程之间的协作。它允许一个或多个线程等待其他线程完成一系列操作后再继续执行。其工作原理类似于Thread.join()方法,但在使用线程池时,CountDownLatch显的更加灵活。
CountDownLatch的构造函数接收一个整型参数,表示计数器的初始值。调用countDown()方法会使计算器的值减一,调用awart()方法会阻塞调用该方法的线程,直到计数器值减为零为止。
1.1 CountDownLatch的工作原理
- 创建一个CountDownLatch实例并设置计数值。
- 启动多个线程,每个线程在完成其任务后调用countDown()方法。
- 主线程调用await()方法,这样主线程会被阻塞,直到计数器的值减为0,随后继续执行。
1.2 常用方法
- public void await() throws InterruptedException: 阻塞当前线程,直到计数器为0。
- public boolean await(long timeout, TimeUnit unit) throws - InterruptedException: 阻塞当前线程,直到计数器为0或超时。
- public void countDown(): 计数器减1。
- public long getCount(): 返回当前计数值。
2 使用场景
- 等待多个线程完成
当一个线程需要等待多个其他线程完成任务才能执行时,可以使用CountDownLatch。例如,等待多个压缩文件全部下载完成后再统一执行解压操作。 - 并发任务的协调
需要并发执行多个任务时,也可以使用CountDownKatch。例如,跑步比赛中,所有运行员需要等待裁判发号施令才能开跑。
2.1 等待多个线程完成的场景
这个场景也可以理解为多个任务完成后汇总的场景,假设有这样一个任务,需要下载多个压缩文件,然后再执行后续的文件解压操作。
import java.util.concurrent.CountDownLatch;
public class FileDownExtractExample {
public static void main(String[] args) {
int numFiles = 3;
CountDownLatch countDownLatch = new CountDownLatch(numFiles);
for (int i = 0; i < numFiles; i++) {
final int finalId = i + 1;
new Thread(() -> {
try {
System.out.println("Downloading file " + finalId);
Thread.sleep(Math.round(Math.random() * 1000));
System.out.println("File " + finalId + " downloaded");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
System.out.println("Waiting for all files to be downloaded...");
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("All files have been downloaded, proceeding with extraction.");
}
}
在这示例中,countDownLatch的值被初始化为3,代表3个文件下载的任务;每个文件下载线程在下载任务完成后都会调用countDown()方法,将计数器减一,当计数器的值减为零时,await()方法结束,主线程可运行,开始执行后续的解压操作。
2.2 并发任务的协调
假设这样一个场景,有多个运动员需要等待裁判的枪声后才能开跑,模拟程序如下:
import java.util.concurrent.CountDownLatch;
public class RaceExample {
public static void main(String[] args) {
int numathletes = 5;
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < numathletes; i++) {
final int athleteId = i + 1;
new Thread(() -> {
try {
System.out.println("Athlete " + athleteId + " is ready");
countDownLatch.await();
System.out.println("Athlete " + athleteId + " starts running");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
try {
//模拟裁判准备时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Bang! Race begins!");
countDownLatch.countDown();
}
}
上面的示例中,首先创建了一个CountDownLatch,初始值为 1,代表裁判的发令操作;然后创建了 5 个运动员线程,每个运动员线程在启动后调用await()方法等待,此时它们都处于阻塞状态;主线程(模拟裁判)在等待 1 秒后,调用countDown()方法,计数器变为零,所有运动员线程被唤醒并开始执行。执行结果如下:
Athlete 4 is ready
Athlete 3 is ready
Athlete 5 is ready
Athlete 2 is ready
Athlete 1 is ready
Bang! Race begins!
Athlete 4 starts running
Athlete 5 starts running
Athlete 3 starts running
Athlete 1 starts running
Athlete 2 starts running
3 总结
CountDownLatch是Java并发编程中一个非常实用的同步工具,它通过简单的计数机制有效地协调多个线程之间的执行顺序。在多线程环境中,合理使用CountDownLatch可以提高程序的可读性和可靠性,使得开发者能够更轻松地管理并发任务。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。