同步工具类
JUC
(Java.util.concurrent)是 Java 提供的用于并发编程的工具类库,其中包含了一些通信工具类,用于在多个线程之间进行协调和通信,特别是在多线程和网络通信方面。这些工具类提供了丰富的功能,帮助开发者高效地实现复杂的并发控制和网络通信需求。
CountDownLatch
CountDownLatch
(倒计时门闩)是 Java 中的一种同步辅助类,用于控制多个线程之间的协调,允许一个或多个线程等待其他线程完成一组操作。
CountDownLatch
通过维护一个计数器来实现,计数器初始值可以设定为一个正整数(通常为需要等待的线程数量或任务数量)。每当一个线程完成任务后,计数器的值就会减1。当计数器的值减到 0 时,所有等待的线程都会被释放,此时所有因调用 await()
方法而阻塞的线程都会被唤醒,继续执行后续的操作。
常用方法
CountDownLatch
提供了几个关键的方法来实现其功能:
-
CountDownLatch(int count)
:构造函数,用于创建 CountDownLatch 实例,并设置计数器的初始值。 -
void countDown()
:递减计数器的值。每当一个线程完成任务后,可以调用此方法将计数器的值减1。 -
void await()
:使当前线程在计数器减至 0 之前一直等待。如果线程在等待过程中被中断,则会抛出 InterruptedException 异常。 -
boolean await(long timeout, TimeUnit unit)
:使当前线程在计数器减至 0 之前等待指定的时间。如果在等待时间内计数器减至 0,则返回 true;如果等待时间超时而计数器仍未减至 0,则返回 false。在等待过程中,如果线程被中断,则会抛出 InterruptedException 异常。 -
long getCount()
:获取当前计数器的值。这个方法主要用于调试或测试目的。
使用步骤
使用 CountDownLatch 的基本步骤如下:
-
创建一个
CountDownLatch
对象,并指定计数器的初始值。 -
在需要等待的线程中,调用
countDown()
方法来减少计数器的值。 -
在等待其他线程的线程中,调用
await()
方法,等待计数器的值减到0。
当计数器的值减到 0 时,所有等待的线程会被唤醒,可以继续执行后续操作。
需要注意的是,CountDownLatch
是一次性的,一旦计数器的值减到 0,就不能重置。如果需要可重复使用的类似功能,可以考虑使用其他同步工具类如 CyclicBarrier
或 Phaser
。
CountDownLatch
在多线程协调和同步的场景中非常有用,可以有效地控制线程的执行顺序和状态的正确性。
适用场景
CountDownLatch
的使用场景是一个或多个线程需要等待其他线程完成某个操作后,再继续执行下去。它常用于以下情况:
-
并行计算的启动屏障:在并行计算中,可能需要等待所有并行任务都准备好后再开始执行。此时,可以将
CountDownLatch
的初始值设置为并行任务的数量,每个任务在开始执行前都调用countDown()
方法,而主线程则调用await()
方法等待所有任务都准备就绪。 -
等待多个线程完成:当有多个线程正在执行,并且需要等待所有这些线程都完成后再继续执行其他操作时,可以使用
CountDownLatch
。通过将计数器的初始值设置为线程的数量,并在每个线程执行完毕后调用countDown()
方法,主线程可以调用await()
方法等待所有线程完成。 -
并发测试:在进行并发测试时,可能需要模拟多个线程同时触发某个操作。此时,可以使用
CountDownLatch
来等待所有线程都准备好后再一起触发操作。
使用示例
以下是一个简单的示例代码,演示了如何使用 CountDownLatch 来等待多个线程完成:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 5;
CountDownLatch latch = new CountDownLatch(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++) {
final int threadNum = i;
new Thread(() -> {
try {
// 模拟任务执行
Thread.sleep(1000);
System.out.println("Thread " + threadNum + " is done.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
}).start();
}
// 等待所有线程完成
latch.await();
System.out.println("All threads have completed.");
}
}
在这个示例中,我们创建了一个包含5个线程的线程池(尽管这里直接使用了 Thread
类来创建线程,但原理相同),每个线程都执行一个模拟任务(通过 Thread.sleep(1000)
实现)。每个线程在完成任务后都会调用 latch.countDown()
来减少计数器的值。主线程调用 latch.await()
等待,直到计数器的值减到 0,即所有线程都完成了任务。然后,主线程继续执行并打印出所有线程都已完成的消息。