10

仮定:

  1. 特定の参照フィールドを設定する特定のスレッドは 1 つだけです (long または double ではないため、そのフィールドへの書き込みはアトミックです)。
  2. 同じフィールドを読み取る可能性のあるスレッドはいくつでもあります
  3. わずかに古い読み取りは許容されます (最大数秒)。

このシナリオでは、volatile または AtomicReference またはそのようなものが必要ですか?

この記事では次のように述べています。

シングル ライターの原則に厳密に従う場合、メモリ バリアは必要ありません。

これは、私が説明しているケースでは、特別なことをする必要がないことを示唆しているようです。

だから、ここに私が実行したテストがあり、興味深い結果が得られました:

import org.junit.Test;

public class ThreadTest {
    int onlyWrittenByMain = 0;
    int onlyWrittenByThread = 0;

    @Test
    public void testThread() throws InterruptedException {
        Thread newThread = new Thread(new Runnable() {
            @Override
            public void run() {
                do {
                    onlyWrittenByThread++;
                } while (onlyWrittenByMain < 10 || onlyWrittenByThread < 10);
                System.out.println("thread done");
            }
        });
        newThread.start();

        do {
            onlyWrittenByMain++;
            // Thread.yield();
            // System.out.println("test");
            // new Random().nextInt();
        } while (onlyWrittenByThread < 10);
        System.out.println("main done");
    }
}

これを実行すると、「スレッドが完了しました」と出力され、永久にハングすることがあります。時々それは完了します。したがって、スレッドはメインスレッドが行った変更を確認しますが、どうやらメインはスレッドが行った変更を常に確認しているとは限りませんか?

システムを投入したり、Thread.yield、またはランダムな呼び出しを行ったり、onlyWrittenByThread を揮発性にしたりすると、毎回完了します (約 10 回以上試行されます)。

それは、私が上で参照したブログ投稿が間違っているということですか? シングルライターのシナリオでもメモリバリアが必要ですか?

誰もこの質問に完全に答えていないので、メモリバリアが必要ないことはおそらく正しいと思いますが、事前発生関係を作成するものがない場合、Java コンパイラとホットスポットは最適化 (巻き上げなど) を行うことができます。したいことをしなくなります。

4

4 に答える 4

2

コードの主な問題は、CPU が何をするかということではなく、JVM が CPU で何をするかということです: 変数巻き上げの危険性が高いです。つまり、JMM (Java メモリ モデル) により、JVM は次のように書き換えることができます。

public void run() {
    do {
        onlyWrittenByThread++;
    } while (onlyWrittenByMain < 10 || onlyWrittenByThread < 10);
    System.out.println("thread done");
}

この他のコードとして (ローカル変数に注意してください):

public void run() {
    int localA = onlyWrittenByMain;
    int localB = onlyWrittenByThread;
    do {
        localB ++;
    } while (localA < 10 || localB < 10);
    System.out.println("thread done");
}

これは、hotpost によって行われるかなり一般的な最適化です。あなたの場合、その最適化が行われると(おそらくそのメソッドを呼び出した直後ではなく、数ミリ秒後に)、他のスレッドで何をしても、そのスレッドからは見えなくなります。

于 2013-06-07T22:37:27.623 に答える
1

読み取りフィールドの変更がリーダー スレッドに表示されない可能性があるため、これが必要です。事前発生関係を作成する必要があります。

于 2013-06-07T18:53:34.223 に答える
1

Martin のブログから引用した言葉を誤解したようですね。x86/64 ハードウェアでは、Atomic*.lazySet を使用して「揮発性」よりもはるかに優れたパフォーマンスを得ることができます。これは、ストア ロード バリアよりもはるかに安価なストア ストア バリアを提供するためです。私の知る限り、代わりに AtomicLong を使用し、lazySet を使用してコードが並べ替えられないようにする必要があります。

詳細については、この記事を参照してください: http://psy-lob-saw.blogspot.com/2012/12/atomiclazyset-is-performance-win-for.html

于 2013-10-31T02:59:06.737 に答える