CountdownLatch await() 没有等待最后一个线程结束

CountdownLatch await() is not waiting for last thread to end

我写了一小段程序来演示 java 中 CountDownLatch class 的用法。 但是,它没有按预期工作。我创建了 5 个线程并为每个线程分配了任务。现在,每个线程都将等待启动信号。一旦开始信号打开,所有线程开始工作并调用 countDown()。现在,我的主线程等待所有线程完成其工作,直到它收到完成信号。但是输出不是预期的。如果我在概念中遗漏任何内容,请提供帮助。 下面是程序。

class Task implements Runnable{
    
    private CountDownLatch startSignal;
    private  CountDownLatch doneSignal;
    private int id;
    
    Task(int id, CountDownLatch startSignal, CountDownLatch doneSignal){
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
        this.id = id;
    }

    @Override
    public void run() {
        try {
            startSignal.await();
            performTask();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    private void performTask() {
        try {
            System.out.println("Task started by thread : " + id);
            Thread.sleep(5000);
            doneSignal.countDown();
            System.out.println("Task ended by thread : " + id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}

public class CountDownLatchExample {

    public static void main(String[] args) {

        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(5);
        for(int i=0; i < 5; ++i) {
            new Thread(new Task(i, startSignal, doneSignal)).start();
        }
        System.out.println("Press enter to start work");
        new Scanner(System.in).nextLine();
        startSignal.countDown();
        try {
            doneSignal.await();
            System.out.println("All Tasks Completed");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出

Press enter to start work

Task started by thread : 0
Task started by thread : 4
Task started by thread : 3
Task started by thread : 2
Task started by thread : 1
Task ended by thread : 4
Task ended by thread : 2
Task ended by thread : 1
All Tasks Completed
Task ended by thread : 0
Task ended by thread : 3

预期输出

Press enter to start work

Task started by thread : 0
Task started by thread : 4
Task started by thread : 3
Task started by thread : 2
Task started by thread : 1
Task ended by thread : 4
Task ended by thread : 2
Task ended by thread : 1
Task ended by thread : 0
Task ended by thread : 3
All Tasks Completed

在您的 Task class 中,您有:

doneSignal.countDown();
System.out.println("Task ended by thread : " + id);

换句话说,在打印“任务结束”之前,您对闩锁倒数。这允许主线程从对 doneSignal.await() 的调用中唤醒并在所有“任务结束之前打印“所有任务已完成” " 打印语句完成。尽管请注意“错误输出”并不总是会发生;有时您会得到预期的输出。

只需切换这两行代码即可保证您想要的输出:

System.out.println("Task ended by thread : " + id);
doneSignal.countDown();

这确保打印语句 happens-before doneSignal.countDown() 调用本身 happens-before来自 doneSignal.await() 的主线程 returns。因此,现在上面的“task ended”打印语句happens-before主线程被唤醒并打印“所有任务已完成”消息。