0
public class NoVisibility {
   private static boolean ready;
   private static int number;
   private static class ReaderThread extends Thread {
      public void run() {
         while (!ready)
            Thread.yield();
         System.out.println(number);
      }
   }

   public static void main(String[] args) {
       new ReaderThread().start();
       number = 42;
       ready = true;
   }
}

「Java Concurrency in Practice」によると、適切な同期を使用しないため、数値への書き込みまたはプログラムがまったく終了しない前に、準備完了への書き込みがリーダースレッドに表示される可能性があるため、0 が出力される可能性があります。メイン スレッドによって書き込まれた ready および number の値がリーダー スレッドに表示されることは保証されていません。

それはどのように可能ですか?プログラムはスレッドによって順次実行され、最初に number に書き込み、次に ready 変数に書き込みます。ではない?そして、このプログラムはどのようにして永遠にループすることができるのでしょうか?

4

2 に答える 2

1

あるスレッドで変数を変更しても、他のスレッドがそれらの変更の結果を見るという保証はありません。技術的には、それらの間に事前発生の関係がないため、保証はありません (実際には、ほぼ常に変更が表示されます)。

そのため、スレッドが永遠に実行される可能性があります。

第二に、なぜ時々 0 ?

まあ、JLSはそれを言います

たとえば、別のスレッドでの読み取りとデータ競合している 1 つのスレッドでの書き込みは、それらの読み取りに対して順不同で発生するように見える場合があります。

つまり、あなたの

number = 42;
ready = true;

任意の順序で発生する可能性があります。順番に表示される可能性が高くなりますが、保証はありません。

変数を揮発性に変更することで修正できます。その場合、書き込みは常にリーダーに表示されるか、状態を変更するコードをクリティカルセクションにすることで修正できます(本を参照)。一般に、あまりにも多くの揮発性変数を使用するのはちょっとしたハックだと思うので、スレッドの「実行中」変数などのために、それらを控えめに使用するようにしてください。

public class NoVisibility {
    private static volatile boolean ready;
    private static volatile int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}
于 2013-06-18T09:37:19.883 に答える