第十六章: Javaのメモリモデル

JVMの話。長かった『Java並行処理』もこれで終わり。長かったー!後半、明らかに手を抜いたなぁ。
ちょっと復習も必要なんだけど、取り敢えず十六章。

安全な遅延初期化のテクニック

良くあるダブルチェックロッキングアンチパターンは、最初に < if (variable == null) return variable; > があるけど、variableが安全にコンストラクトされているとは限らないので使わない方が良いよね、って話。
遅延初期化にも色々方法があるけど・・・。

まず、駄目な例。

// 素朴なパターン
public static class UnsafeLazyInitialization {

	private static UnsafeLazyInitialization instance;

	public static UnsafeLazyInitialization getInstance() {
		if (instance == null) {
			instance = new UnsafeLazyInitialization();
		}
		return instance;
	}

}

// ダブルチェックロッキングアンチパターン
public static class DoubleCheckedLocking {

	private static DoubleCheckedLocking instance;

	public static DoubleCheckedLocking getInstance() {
		if (instance == null) {
			synchronized (DoubleCheckedLocking.class) {
				if (instance == null) {
					instance = new DoubleCheckedLocking();
				}
			}
		}
		return instance;
	}

}

スレッドセーフにしてみる。

// 同期化するがちょっとコストが高い。
public static class SafelyLazyInitialization {
	
	private static SafelyLazyInitialization instance;
	
	public synchronized SafelyLazyInitialization getInstance() {
		if (instance == null) {
			instance = new SafelyLazyInitialization();
		}
		return instance;
	}
	
}

// クラスを読み込んだ時点で初期化するパターン。
public static class EagerInitialization {
	
	private static EagerInitialization instance = new EagerInitialization();
	
	public static EagerInitialization getInstance() {
		return instance;
	}
	
}

で、最終的には以下のパターンが有効。

public static class LazyValue {

	private static class LazyValueHolder {
		public static LazyValue value = new LazyValue();
	}

	public static LazyValue getInstance() {
		return LazyValueHolder.value;
	}

}

おー、これはなかなか頭が良い、と思った。
EagerInitializationのようにクラスが読まれた時点ではまだ初期化されず、getInstance() を呼び出した時点で初めてLazyValueHolderクラスが読み込まれ、staticイニシャライザで初期化される。