使用CountDownLatch来替代join

本文比较了CountDownLatch和join()在多线程编程中的应用,强调了CountDownLatch的灵活性、可复用性和对主线程影响较小的特点,指出CountDownLatch更适合复杂任务协调。
摘要由CSDN通过智能技术生成

`CountDownLatch` 和 `join()` 方法都是用于在多线程编程中实现线程间的同步。它们有着类似的功能,但是在某些场景下,`CountDownLatch` 相较于 `join()` 具有一些优点:

1. **更灵活的等待条件**:
   - `CountDownLatch` 允许主线程等待多个子线程完成任务。主线程可以等待多个线程的同时完成,而不需要一个一个地等待每个线程。
   - `join()` 方法只能等待一个线程完成任务,如果有多个线程需要等待,必须分别调用多次 `join()`,使得代码变得冗长。

2. **可复用性**:
   - `CountDownLatch` 可以被复用,即在一个计数器归零后,可以重置计数器,再次使用。
   - `join()` 方法一旦被调用,就无法被重复使用。如果一个线程已经被 `join()`,再次调用 `join()` 会抛出 `IllegalThreadStateException`。

3. **不影响线程的状态**:
   - `join()` 方法会阻塞主线程,直到被等待线程完成或者超时。这可能会影响主线程的执行,特别是在GUI或者服务器程序中,可能导致界面冻结或者响应时间延长。
   - `CountDownLatch` 不会阻塞主线程,主线程可以继续执行其他任务,只是在需要等待的时候调用 `await()` 方法等待。

4. **更灵活的任务协调**:
   - `CountDownLatch` 允许子线程在完成任务后继续执行其他操作,而不需要等待主线程。这样可以更灵活地安排任务执行的顺序和协作方式。
   - `join()` 方法会阻塞主线程,直到被等待线程完成任务后,主线程才能继续执行。这可能会导致线程间的协作受限。


/**
 * 作为线程同步使用,除非计数器变成了0 ,否则永远不会停止,可以保证所有的任务都做完  和join的方式类似,但是join是底层的api,用起来过于繁琐
 */
@Slf4j(topic = "c.TestCountDownLatch")
public class TestCountDownLatch {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        ExecutorService service = Executors.newFixedThreadPool(4);
        service.submit(()->{
            log.debug("begin");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.debug("end...{}",latch.getCount());
        });
        service.submit(()->{
            log.debug("begin");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.debug("end...{}",latch.getCount());
        });
        service.submit(()->{
            log.debug("begin");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.debug("end...{}",latch.getCount());
        });
        service.submit(()->{
            try {
                log.debug("waiting");
                latch.await();
                log.debug("waiting end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

    }
}

`final CountDownLatch countDownLatch = new CountDownLatch(messageCount);` 这段 Java 代码创建了一个 `CountDownLatch` 实例,并将其命名为 `countDownLatch`。这里详细解释一下 `CountDownLatch` 和这个初始化过程: ### `CountDownLatch` 的作用 `CountDownLatch` 是 Java 并发 API 中的一个工具类,用于控制对共享资源的访问并发线程的数量限制。当所有等待线程都被通知到的时候(即等待线程的数量减少至0),`CountDownLatch` 会解除所有等待线程的阻塞状态,它们可以继续执行。 ### 初始化参数 `messageCount` 在上述代码中,`messageCount` 参数是指定了等待线程的最大数量。在这个上下文中,通常意味着有若干条线程需要完成各自的任务(例如发送消息、处理数据等)。一旦所有这些线程都完成了它们的工作并调用了 `CountDownLatch` 的 `countDown()` 方法,那么 `countDownLatch` 就会被 "关闭" 或者说 "倒计时完毕",这时处于等待的线程就会被唤醒,开始后续的操作(如执行回调函数、进入下一轮循环等)。 ### 示例用途 想象有一个场景,你需要同时启动 n 条消息发送任务。你可以为每个消息发送任务创建一个单独的线程,并将这些线程的所有实例存储在一个列表中。之后,在主程序中,你可以通过调用 `await()` 方法来让主线程等待直到所有的消息发送线程都已经完成其工作。这实际上是利用了 `CountDownLatch` 的功能来同步线程的执行顺序和控制并发操作的执行时间。 ```java List<Thread> threads = new ArrayList<>(); for (int i = 0; i < messageCount; i++) { Thread thread = new Thread(() -> sendMessage(i)); threads.add(thread); thread.start(); } CountDownLatch latch = new CountDownLatch(messageCount); threads.forEach(t -> t.join(latch)); ``` 在这段代码示例中,我们首先启动了 n 个线程(`sendMessage(i)` 表示每个线程负责发送一条特定的消息),然后我们创建了一个 `CountDownLatch` 对象并将初始值设为了 n(`messageCount`)。当所有线程都结束(`thread.join(latch)` 等待所有线程结束)并且 `sendMessage(i)` 函数完成后,我们将调用 `latch.countDown()` 来释放所有等待的线程,从而使整个程序能够继续执行下一步流程。 ### 相关问题: 1. `CountDownLatch` 的原理是什么? 2. 如何安全地使用 `CountDownLatch` 避免死锁情况? 3. `CountDownLatch` 是否能替代其他并发控制机制,如 `Semaphore` 或 `ReentrantLock`? --- 以上就是关于如何使用 `CountDownLatch` 及其初始化参数的解释。希望这对您理解并发编程中的这一重要工具有所帮助。如果您需要进一步的代码示例或更深入的理论知识,欢迎提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值