2012-11-27 20 views
1

运行每个线程时为什么即使在前一个线程已经调用countdown.countDown()并将Latch Count减少1之后,为什么countdown.getCount()始终会打印'3'?Java - COUNTDOWNLATCH中的计数

我有点厌倦了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"); 
} 

}

+0

上一个线程已经调用countdown.countDown()之后,逻辑错误出现在*中*:它可能没有 – assylias

+2

http://ideone.com/EloGi9 - 在那里工作正常。如果你幸运,它的行为就像你期望的那样。这就是未定义的多线程行为的美妙之处 – zapl

回答

2

你开始并行3个线程。取决于他们开始的速度,他们可以在任何线程管理呼叫countDown()(至少对于“Starting on ...”行)之前打印“3”。但是,“new on ...”行应打印出2到0之间的某些范围的数字。

+0

好吧,如果它全部开始,那么Java如何知道(更新锁存计数)线程可以等待释放? – user547453

+0

@ user547453 - 我很困惑你的问题。当latch达到0时,'await()'调用返回,一旦所有的线程都调用了'countDown()',就会立即返回。 – jtahlborn

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()); 

有时你会得到一个输出像下面这可能给你的印象是主题-2忽视线程1的号召,倒计时:

00> 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)。计数值是同步的,等待线程只在计数变为0或其他线程中断等待线程时才被释放。这是闩锁用来跟踪所有线程何时到达闩锁的机制。

如果您有兴趣了解测试环境中的锁存器,请参阅http://razshahriar.com/2014/08/testing-asynchronous-code-in-java-with-countdownlatch/ 希望这有助于澄清您对本程序行为的调查。