先程、これの10章を読み終えました。

- 作者: Joshua Bloch
- 出版社/メーカー: Prentice Hall
- 発売日: 2008/05/08
- メディア: ペーパーバック
- 購入: 6人 クリック: 65回
- この商品を含むブログ (42件) を見る
284ページの内容がちょっと面白かったので、ご紹介します。
(284ページの意訳)
JDK5からはメモリモデルのあいまいさが減ったため、volatileが正しく動作するようになった。初期化が何回か実行されてしまうことを許容できるなら、遅延ロードをこんな風に書いても大丈夫になったよ。
(コード1)
// Single-check idiom - can cause repeated initialization! private volatile FieldType field; private FieldType getField() { FieldType result = field; if (result == null) field = result = computeFieldValue(); return result; }
とあります。
さて、このコードは奇妙だ。
ローカル変数resultが無意味に見えるんだけど。
これと何が違うんだ?
(コード2)
/* 1 */ private volatile FieldType field; /* 2 */ /* 3 */ private FieldType getField() { /* 4 */ if (field == null) /* 5 */ field = computeFieldValue(); /* 6 */ /* 7 */ return field; /* 8 */ }
…などと思いつつ暫く悩んでいたわけですが、やっとresultの意味が判りました。
コード1とコード2では、一点だけ違いがあるのです。
何が違う?
例えば、二つのスレッドが同時に、コード2におけるgetFieldを実行したときのワーストケースを考えてみます。
初期状態でfieldはnullとします。
- スレッド1: 4行目、チェックをパスする。
- スレッド1: 5行目、computeFieldValue()の結果(値Aとする)をスタックに積む。
(fieldはまだnullのまま) - スレッド2: 4行目、チェックをパスする。
- スレッド2: 5行目、computeFieldValue()の結果(値A'とする)をスタックに積む。
(fieldはまだnullのまま) - スレッド1: 5行目、値Aをfieldへ格納する。
- スレッド2: 5行目、値A'をfieldへ格納する。
- スレッド1: 7行目、fieldの値、すなわち値A'をリターンする。
- スレッド2: 7行目、fieldの値、すなわち値A'をリターンする。
上記のステップ7で、スレッド1は自分で計算した値Aでなく、値A'を返してしまうコトになります。
コード1の場合、ローカル変数resultへ計算結果を退避しているため、ワーストケースでもスレッド1は値A、スレッド2は値A'を返すことができます。
実際には、このようなケースでは値Aと値A'は完全に等価であることがほとんどだと思うので、
コード2のように書いても全く問題ないんですけどね。