You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中CountDownLatch为何未按预期计数停止并触发后续逻辑?

为什么CountDownLatch的"Completed."没有在第6次"Started."后输出?

先把你提供的代码格式化一下,方便查看:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Processor implements Runnable {
    private CountDownLatch latch;

    public Processor(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        System.out.println("Started.");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        latch.countDown();
    }
}

public class App {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(5); // 从5倒数到0
        ExecutorService executor = Executors.newFixedThreadPool(2); // 线程池有2个线程

        for(int i=0; i < 10; i++) {
            executor.submit(new Processor(latch)); // 每次提交任务,任务完成时会调用countDown
        }

        try {
            latch.await(); // 等待latch倒数到0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Completed.");
    }
}

你提到预期是当计数从5减到0时,"Completed."应该在第6次"Started."后输出,但实际总是在第7或8次之后出现。这其实是因为你混淆了任务启动(打印"Started.")和countDown()执行的时机,我来给你拆解一下整个流程:

核心误解点

你以为countDown()是在任务打印"Started."的时候执行,但实际上,每个任务的执行顺序是:

  1. 打印"Started." → 任务启动
  2. 休眠3秒 → 模拟耗时操作
  3. 才调用countDown() → 让latch的计数减1

而你的线程池只有2个线程,任务是排队执行的,我们一步步理清楚时间线:


第0-3秒:

  • 线程池的2个线程同时启动任务1和任务2,分别打印Started.(第1、2次),然后进入3秒休眠。
  • 剩下的8个任务在队列里等待执行。

第3秒左右:

  • 任务1的休眠结束,调用countDown() → latch从5变成4。
  • 线程1立刻去执行队列里的任务3,打印Started.(第3次),然后休眠3秒。
  • 任务2的休眠结束,调用countDown() → latch从4变成3。
  • 线程2立刻去执行队列里的任务4,打印Started.(第4次),然后休眠3秒。

第6秒左右:

  • 任务3的休眠结束,调用countDown() → latch从3变成2。
  • 线程1执行任务5,打印Started.(第5次),休眠3秒。
  • 任务4的休眠结束,调用countDown() → latch从2变成1。
  • 线程2执行任务6,打印Started.(第6次),休眠3秒。

第9秒左右:

  • 任务5的休眠结束,调用countDown() → latch从1变成0!此时主线程的latch.await()被唤醒,开始打印Completed.。
  • 线程1立刻去执行队列里的任务7,打印Started.(第7次),然后休眠3秒。
  • 任务6的休眠结束,调用countDown() → 此时latch已经是0了,这个操作不会有任何影响。
  • 线程2执行任务8,打印Started.(第8次),然后休眠3秒。

所以你看,当latch计数到0的时候,线程1已经开始执行任务7了,Completed.会和第7次(甚至第8次)的Started.交替出现,这完全取决于JVM的线程调度顺序,这就是为什么你看到的结果总是在第7或8次之后出现。

验证你的预期

如果你想让Completed.在第5次countDown()后立刻出现,也就是在第6次Started.之后,你可以把countDown()移到Thread.sleep()之前,也就是任务启动后立刻减计数:

public void run() {
    System.out.println("Started.");
    latch.countDown(); // 移到休眠前
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

这时候,前5个任务启动后就会把latch减到0,主线程会在第5次Started.之后(也就是第6次Started.之前或同时)打印Completed.,符合你最初的预期。

内容的提问来源于stack exchange,提问作者iramshiv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:50:14