4

私は以下のコードを持っています

class VolatileCount {
    volatile int count;
    Object lock = new Object();

    public void increment() {

        synchronized (lock) {
            count = count + 1;
        }
        System.out.print(" " + count);
    }

}

複数のスレッドから同じオブジェクトを呼び出すとincrement()、次の出力が得られます (マシンによって異なる場合があります)。

2 3 2 5 4 8 8 6 11 13 10 9 15 14 12 20 19

最初の 3 つの数値(2 3 2) を考慮すると、スレッドが 3 を認識した場合、インクリメントが発生し、変数が揮発性であるため、その値は 3 以上である必要がありますがどのスレッドでも 2 にすることはできません。
ただ、ここで印刷行が並び替えられているようですが、その行を並び替えて正しいでしょうか?ここで何が欠けていますか?私はJDK 7(Eclipse)で実行しています

4

4 に答える 4

7

アップデート

「2 3 2」の特定のケースについて説明が必要なようですので

  • X インクリメントi(現在は 1)
  • Y インクリメントi(現在は 2)
  • X 読み取りi(X ロード 2)
  • Y 読み取りi(Y 読み込み 2)
  • Y は、以前にロードされた値を出力します (出力 2)
  • Zインクリメントi、読み取りi、印刷i(印刷3)
  • X は、以前にロードされた値を出力します (出力 2)

ポイントはSystem.out.print(" " + count)、アトミックではないということです。揮発性読み取りを実行した後、値を出力する前に、スレッドを横取りすることができます。

重複した値が出力されるのを防ぎたい場合は、ロック内で揮発性読み取りを実行する必要があります。

public void increment() {
    int localCount;
    synchronized (lock) {
        count = count + 1;
        localCount = count; // volatile load
    }
    System.out.print(" " + localCount);
}

ただし、これは値が順不同で出力されるのを防ぐものではありません。それらを重複せずに順番に印刷するには、printもロックに移動する必要があります。

古い回答

print ステートメントがロックの外側にあります。内部で 実行されているコードを考えてみましょうSystem.out.print(" " + count)

  • スレッド X が増加するi
  • スレッド X は引数を評価し、変数に対してprint揮発性読み取りを実行し、値をロードします。count2
  • スレッド X はスレッド Y によってプリエンプトされ、スレッド Y はインクリメントします。i
  • スレッド Y が読み込まiれ (現在は3)、完了まで実行される print メソッドが呼び出されます。
  • スレッド Y がプリエンプトされ、スレッド X が最後まで実行printされ、2 が出力されます。

これにより、「3 2 4」のように、数字が順不同で表示されます。

一部の数字は、次の場合にも繰り返される場合があります。

  • スレッド Xiのインクリメント (現在は 2)
  • スレッド Yiのインクリメント (現在は 3)
  • スレッド X プリントi(つまり 3)
  • スレッド Y の印刷i(つまり 3)
于 2014-01-29T10:42:40.097 に答える
1

IDE が出力をバッファリングする可能性があるため、問題はSystem.out.printマルチスレッドにある可能性があります。このディスカッションを確認し、コマンド ラインから実行してみてください。

プリントをロックの中に入れれば、問題は解決するはずです

于 2014-01-29T10:54:19.243 に答える
0

印刷物が保護ゾーンの外にあります。つまり、カウントがフェッチされた後、System.out に送信される前など、インクリメントと出力の間の任意の時点で中断される可能性があります。これを同期ブロック内に移動すると、期待どおりの動作が見られると思います。

揮発性は同期の代わりにはなりません。値はいつでも変更される可能性があるため、最適化されるのではなく、参照されるたびにフェッチする必要があることをコンパイラに警告するだけです。

于 2014-01-29T10:47:57.420 に答える