5

からダブル チェック ロックについて読んでいEffective Javaます。コードは次のことを行います。

private volatile FieldType field;  
FieldType getField() {  
    FieldType result = field;  
    if (result == null) { // First check (no locking)  
        synchronized(this) {   
        result = field;  
        if (result == null) // Second check (with locking)  
            field = result = computeFieldValue();  
        }  
    }  
    return result;  
}    

を使用する必要はないresultように見えますが、実際には、field既に初期化されている一般的なケースで一度だけ読み取られることが保証されます。

しかし、私はこれを理解していません。if(field == null)直接行うこととの違いは何ですか?if (result == null)述べたように優れていることは言うまでもなく、 なぜ異なるのかわかりません。

4

2 に答える 2

5

説明は次のページにあります(私による強調):

この変数が行うことは、フィールドが既に初期化されている一般的なケースで一度だけ読み取られるようにすることです。厳密には必要ではありませんが、これによりパフォーマンスが向上する可能性があり、低レベルの並行プログラミングに適用される標準により洗練されています。私のマシンでは、上記の方法は、ローカル変数を使用しない明らかなバージョンよりも約 25% 高速です。

参考までに、引用はp. 項目 71 の 284:有効な Java 2nd Edition では遅延初期化を慎重に使用してください。

更新: ローカル変数と揮発性変数の読み取りの違いは、前者の方が最適化されている可能性があることです。揮発性変数をレジスタまたはキャッシュに格納することも、メモリ操作を並べ替えることもできません。さらに、揮発性変数を読み取ると、異なるスレッド間でメモリ同期がトリガーされる場合があります。

詳細については、 Java Concurrency in Practiceのセクション 3.1.4:揮発性変数を参照してください。

于 2012-05-09T09:01:20.517 に答える
4

その例の考え方は、結果/フィールドがさらに下で複数回使用されるということです。アクセスresultは安価です (揮発性ではありません)。

それ以外の場合は、リターンを行うときに2回目の揮発性読み取りがあります。

これを行う必要がある場合は、代わりに初期化オンデマンド ホルダー パターンを使用してください。 http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

コメントに私の説明のいくつかを回答自体に追加して、わかりやすくします:

ショートバージョン:ローカル変数は、CPU(の1つ)(および複数の場合はCPUのコアの1つなど)のレジスタにあるだけです。それはそれが得るのと同じくらい速いです。volatile 変数は、他のコア/キャッシュ/CPU/メモリの変更をチェックする必要がありますが、詳細は非常にハードウェア固有の場合があります (キャッシュ ライン、メモリ バリアなど)。しかし、jvm 固有でもあり (たとえば、ホットスポット サーバー コンパイラは非揮発性変数をホイストする可能性があります)、パフォーマンス向上のために命令の並べ替えに制限を課します。

于 2012-05-09T09:02:47.433 に答える