仮定:
- 特定の参照フィールドを設定する特定のスレッドは 1 つだけです (long または double ではないため、そのフィールドへの書き込みはアトミックです)。
- 同じフィールドを読み取る可能性のあるスレッドはいくつでもあります
- わずかに古い読み取りは許容されます (最大数秒)。
このシナリオでは、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 コンパイラとホットスポットは最適化 (巻き上げなど) を行うことができます。したいことをしなくなります。