1

各スレッドの実行中に、前のスレッドが既に countdown.countDown() を呼び出してラッチカウントを 1 減らした後でも、countdown.getCount() が常に「3」を出力するのはなぜですか?

3 つのスレッドすべてを解放できるように、ラッチ カウントが 0 に達したことを Java がどのように認識するかについては、ちょっとうんざりしています。

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

スレッドが並行して実行され、スレッドが countDown() を実行するまでカウントが変化しないため、3 つのスレッドすべてが「Starting on 3..」を出力する可能性は十分にあります。何が起こっているのかを本当に理解するために、以下のように、print ステートメントの前に 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 を参照)。カウントの値は同期され、待機中のスレッドは、カウントが 0 になるか、別のスレッドが待機中のスレッドに割り込んだ場合にのみ解放されます。これは、すべてのスレッドがいつラッチに到達したかを追跡するためにラッチによって使用されるメカニズムです。

テストのコンテキストでラッチを理解することに興味がある場合は、http://razshahriar.com/2014/08/testing-asynchronous-code-in-java-with-countdownlatch/をチェック してください。プログラムの動作。

于 2014-08-08T09:41:05.947 に答える