5

「インスタンスフィールドの遅延初期化のためのダブルチェックイディオム」を検討してください。

// 効果的な Java の項目 71 は、この Bloch へのインタビューからコピーしたものです。
プライベートな揮発性 FieldType フィールド。
FieldType getField() {
    FieldType 結果 = フィールド。
    if (result == null) { // 最初のチェック (ロックなし)
        同期(これ){
            結果 = フィールド;
            if (result == null) // 2 番目のチェック (ロックあり)
                フィールド = 結果 = computeFieldValue();
        }
    }
     結果を返します。
}

フィールドを安全な方法でリセットできるようにしたい (私の場合は、データベースから強制的に再度ロードする)。reset メソッドを使用することでこれを行うことができると思います。

ボイドリセット() {
   フィールド = null;
}

これは、フィールドをリセットする標準的な方法ですか? 安全ですか?落とし穴はありますか?ブロッホがダブルチェック遅延読み込みについて次の警告を出したので、私が尋ねているのです。良い考えではありませんが、ここでは適切です。」

ヒマラヤのプラヤさん、よろしくお願いします。

4

5 に答える 5

4

はい、これはスレッドセーフです。

同期ブロックは、複数のスレッドが不必要に を呼び出すのを防ぐためのものcomputeFieldValue()です。fieldは揮発性であるため、 および へのアクセスはresetすべてgetField整然としています。

最初のチェックが null でない場合は、getField実行されます。result返されます。

それ以外の場合は、ロックが取得され、フィールドを非 null に設定する可能性のある他のスレッドは除外されますが、任意のスレッドがfieldnull に設定されることは許可されます。いずれかのスレッドがfieldnull に設定されている場合、何も変更されていないはずです。これが、スレッドを同期ブロックに入れた条件です。現在のスレッドのチェック後に別のスレッドがすでにロックを取得しており、フィールドに null 以外の値が設定されている場合、2 番目のチェックでそれが検出されます。

于 2008-11-20T23:01:17.490 に答える
3

これは安全だと思いますが、フィールドをローカル変数に格納しているからです。これが完了すると、別のスレッドがフィールドの値を途中でリセットしたとしても、ローカル変数参照が魔法のように null に変更されることはありません。

于 2008-11-20T22:37:28.800 に答える
0

reset() メソッドが正しくないと思います。項目 71 を読むと、次のことがわかります。

このコードは少し複雑に見えるかもしれません。特に、ローカル変数の結果の必要性が不明な場合があります。この変数が行うことは、フィールドが 既に初期化されている一般的なケースで一度だけ読み取られるようにすることです。

遅延初期化は、そのフィールドが変更される可能性があるとは想定していません。これらの演算子の間でフィールドが null に設定される場合:

FieldType result = field;
if (result == null) { // First check (no locking)

getField() は正しくない結果を返します。

于 2013-01-23T10:41:31.953 に答える
0

スレッドセーフの意味に正確に依存していると思います。

最初のインスタンスが 2 番目の後に使用される状況になる可能性があります。それは大丈夫かもしれませんし、そうでないかもしれません。

于 2008-11-26T13:06:45.237 に答える
0

リセット方法が上記の方法であれば、これでうまくいきそうですreset()。しかし、reset()メソッドが新しいオブジェクトをインスタンス化する場合 (以下のように)、意図したものとは異なるものを返す可能性はありませんか?

void reset() {
    field = new FieldType();
}
于 2008-11-20T22:55:53.413 に答える