これは に関する JavaDoc からの抜粋ですConcurrentHashMap
。取得操作は一般にブロックされないため、更新操作と重複する可能性があると書かれています。これは、メソッドがスレッドセーフではないことを意味しますかget()
?
ただし、すべての操作がスレッドセーフであるにもかかわらず、取得操作ではロックが伴わず、すべてのアクセスを防止する方法でテーブル全体をロックする機能はサポートされていません。このクラスは、スレッドセーフに依存し、同期の詳細に依存しないプログラムでは、Hashtable と完全に相互運用可能です。
取得操作 (get を含む) は通常ブロックされないため、更新操作 (put および remove を含む) と重複する場合があります。取得は、開始時に保持されている最新の完了した更新操作の結果を反映します。"
ベストアンサー1
このget()
方法はスレッドセーフであり、他のユーザーがこの特定の問題に関して役立つ回答を提供しました。
しかし、ConcurrentHashMap
スレッドセーフではあるが立ち寄りの置き換えとしてHashMap
、複数の操作を実行する場合はコードを大幅に変更する必要がある場合があることを認識することが重要です。たとえば、次のコードを見てみましょう。
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
マルチスレッド環境では、これは競合状態です。ConcurrentHashMap.putIfAbsent(K key, V value)
戻り値に注目してください。戻り値は put 操作が成功したかどうかを示します。詳細についてはドキュメントをお読みください。
なぜこれが競合状態なのかを明確にするよう求めるコメントに回答します。
2 つのスレッドがありA
、それぞれ同じキーを持つB
2 つの異なる値をマップに格納するとします。キーは最初はマップに存在しません。これらは次のようにインターリーブされます。v1
v2
- スレッド
A
が呼び出されcontainsKey
、キーが存在しないことが検出されますが、すぐに中断されます。 - スレッドは
B
呼び出してcontainsKey
、キーが存在しないことを確認し、その値を挿入する時間がありますv2
。 - スレッドは
A
再開して を挿入し、スレッド によって挿入された値をv1
「平和的に」上書きします ( はスレッドセーフであるため) 。put
B
スレッドはB
、独自の値 を正常に挿入したと「考え」ますv2
が、マップには が含まれていますv1
。これは本当に悲惨なことです。スレッドはB
を呼び出しv2.updateSomething()
、マップの消費者 (他のスレッドなど) がそのオブジェクトにアクセスでき、おそらく重要な更新 (「この訪問者の IP アドレスは DOS を実行しようとしているので、今後すべての要求を拒否する」など) を確認すると「考え」ます。代わりに、オブジェクトはすぐにガベージ コレクションされ、失われます。