9

並行性に戻ります。double checked lockingが機能するには、変数を として宣言する必要があることは明らかですvolatile。しかし、以下のようにダブルチェックロックを使用するとどうなるでしょうか。

class Test<A, B> {

    private final Map<A, B> map = new HashMap<>();

    public B fetch(A key, Function<A, B> loader) {
        B value = map.get(key);
        if (value == null) {
            synchronized (this) {
                value = map.get(key);
                if (value == null) {
                    value = loader.apply(key);
                    map.put(key, value);
                }
            }
        }
        return value;
    }

}

通常のHashMapではなく、実際にConcurrentHashMapである必要があるのはなぜですか? すべてのマップの変更はブロック内で行われ、コードは反復子を使用しないため、技術的には「同時変更」の問題はありません。synchronized

API の使用ではなく概念について質問しているため、 putIfAbsent/の使用を提案しないでください:) この API の使用がvsの主題に寄与しない限り。computeIfAbsentHashMapConcurrentHashMap

2016-12-30 更新

この質問は、以下の Holger によるコメントによって回答されました。「HashMap.get構造は変更されませんが、あなたの の呼び出しは変更されます。同期ブロックの外側で のput呼び出しがあるため、操作の不完全な状態が同時に発生していることがわかります。」ありがとう!getput

4

1 に答える 1

17

この質問は非常に多くの点で混乱しているため、答えるのが難しい.

このコードが 1 つのスレッドからしか呼び出されない場合は、コードが複雑になりすぎています。同期は必要ありません。しかし、明らかにそれはあなたの意図ではありません。

そのため、複数のスレッドが fetch メソッドを呼び出し、同期なしで HashMap.get() に委譲します。HashMap はスレッドセーフではありません。バム、話の終わり。二重チェックのロックをシミュレートしようとしているかどうかは問題ではありません。現実には、マップ上でと を呼び出すget()と、 の内部可変データ構造が操作され、すべてのコード パスで一貫した同期が行われず、これらを複数のスレッドから同時に呼び出すことができるため、すでに死んでいます。put()HashMap

(また、おそらく純粋な読み取り操作だと思うかもしれませんHashMap.get()が、それも間違っています。HashMap が実際に LinkedHashMap (HashMap のサブクラス) である場合はどうなるでしょうか。) LinkedHashMap.get() はアクセス順序を更新します。これには書き込みが含まれます。内部データ構造 -- ここでは、同期せずに同時に実行します。ただし、get() が書き込みを行っていない場合でも、ここのコードはまだ壊れています。)

経験則: 同期を避けるための巧妙なトリックを思いついた場合、それはほぼ確実に間違っています。

于 2015-10-26T16:45:08.897 に答える