第三章 (1): 可視性

1時半就寝・8時起床。寝坊しました・・・。

オブジェクトを共有する際の可視性について

シングルスレッドでは変数にある値を書き込み、次にその変数を読み込めば、書き込んだ値を得ることが出来る。しかしマルチスレッドでreadとwriteが別々のスレッドで行われると、この前提が崩れることがある。複数のスレッドで同期化せずに変数を共有するとこのような現象が発生する。

public class VisibilitySample {

	private static boolean ready;
	private static int count;

	public static void main(String[] args) {

		// Read Thread
		new Thread() {
			@Override
			public void run() {
				while (!ready) {
					Thread.yield();
				}
				System.out.println("read Thread: " + count);
			}
		}.start();

		// Main Thread (Write Thread)
		count = 10;
		ready = true;
		System.out.println("write Thread: " + count);

	}

}

このコードでは、同期化せずに変数を共有しているので、readyの値がいつまでもReadThreadから可視にならず、無限ループしたり、readyへのtrueの書き込み処理がcountへの書き込みよりも先に見えたので、ゼロを出力する可能性もある。
これはメモリの可視性の問題である。あるスレッドが別のスレッドの変数を見るとき、読むスレッドは書き込んだスレッドと同じ順序で読む保証は無い。読むスレッドは古い値(陳腐化したデータ)を見ることもある。

揮発性変数: volatile

揮発性変数として扱うためにvolatilieキーワードで修飾すると、他のメモリとの間で順序変えをしなくなる。揮発性変数に対するreadは常に、あるスレッドが書き込んだ最新の値を返す。
volatileはsynchronizedより軽量な同期化の仕組みで、スレッドをブロックしない。ただしvolatileはそれを使った方が読みやすいようなときだけ使うべき。例えばオブジェクトのステートを表す変数の可視性を確保するときなどが使われどころ。なお、volatileはread-modify-writeのような一連の操作をアトミックに実行出来ないので、このような場合にはアトミッククラスを使うべき。