1

在运行每个线程时,为什么即使在前一个线程已经调用 countdown.countDown() 并将 Latch Count 减 1 之后,countdown.getCount() 总是打印“3”?

我有点担心 Java 如何知道 Latch Count 已达到 0,以便它可以释放所有 3 个线程。

import java.util.concurrent.CountDownLatch;

class b {
static final CountDownLatch countdown = new CountDownLatch(3);

public static void main(String[] args) {

    for (int i = 0; i < 3; ++i) {
        Thread t = new Thread() {
            public void run() {
                System.out.printf("Starting on %d other threads.\n",
                        countdown.getCount());
                countdown.countDown();
                System.out.printf("new on %d other threads.\n",
                        countdown.getCount());
                try {
                    countdown.await(); // waits until everyone reaches this
                                        // point
                    // System.out.println("Go again : "
                    // +countdown.getCount());
                } catch (Exception e) {
                }
            }
        };
        t.start();

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

}

4

2 回答 2

2

您正在并行启动 3 个线程。根据它们启动的速度,它们都可以在任何线程设法调用之前打印“3” countDown()(至少对于“Starting on ...”行)。然而,“new on ...”行应该打印出介于 2 和 0 之间的某个数字范围。

于 2012-11-27T20:12:00.750 回答
0

当线程并行运行时,所有三个线程都打印“Starting on 3..”是绝对有可能的,并且直到线程执行countDown(),计数才会改变。要真正了解发生了什么,我建议您在打印语句之前添加 System.nanoTime() 和线程名称,如下所示:

...
Thread t = new Thread("Thread-" + i) { 
...
System.out.printf("%d> %s: Starting on %d other threads.\n", System.nanoTime(), getName(), countdown.getCount());
countdown.countDown();
System.out.printf("%d> %s: new on %d other threads.\n", System.nanoTime(), getName(), countdown.getCount());

有时您会得到如下所示的输出,这可能会让您觉得 Thread-2 忽略了 Thread-1 对 countDown 的调用:

1407489646569321000> Thread-0: Starting on 3 other threads.
1407489646569324000> Thread-1: Starting on 3 other threads.
1407489646602463000> Thread-1: new on 1 other threads.
1407489646569513000> Thread-2: Starting on 3 other threads.
1407489646602107000> Thread-0: new on 2 other threads.
1407489646603275000> Thread-2: new on 0 other threads.

然而,事实并非如此,我们可以通过查看时间戳来验证正确的操作顺序。输出中的混淆是由于线程调度中固有的不可预测性,具体取决于哪个线程获得 cpu 拼接。

话虽如此,它们可能并不总是打印 3,这取决于线程调度或延迟。例如,尝试放置一个 Thread.sleep(..) ,如下所示:

public static void main(String[] args) throws Exception {
    for (int i = 0; i < 3; ++i) {
        Thread t = new Thread() {
            public void run() {
                 /* As before */
            }
        };
        t.start();
        Thread.sleep(100); // Artificial Delay
    }
}

现在你应该看到不同的结果,如下所示:!

1407490223575404000> Thread-0: Starting on 3 other threads.
1407490223607879000> Thread-0: new on 2 other threads.
1407490223676233000> Thread-1: Starting on 2 other threads.
1407490223676818000> Thread-1: new on 1 other threads.
1407490223777623000> Thread-2: Starting on 1 other threads.
1407490223778221000> Thread-2: new on 0 other threads.

在内部,CountDownLatch 维护一个先进先出的等待队列(参见 AbstractQueuedSynchronizer)。count的值是同步的,等待的线程只有在count变为0或者其他线程中断等待线程时才会释放。这是闩锁用来跟踪所有线程何时到达闩锁的机制。

如果您有兴趣了解测试上下文中的闩锁,请查看http://razshahriar.com/2014/08/testing-asynchronous-code-in-java-with-countdownlatch/ 希望这有助于澄清您对程序行为。

于 2014-08-08T09:41:05.947 回答