4

私は最近、HashMap を拡張して put メソッドを同期するクラスをコード ベースで見つけました。

ConcurrentHashMap を使用するよりも効率が悪いことを除けば、HashMap を拡張して put(K,V) のみを同期すると、どのような問題が発生する可能性がありますか?

get(K) が最新の値を返すかどうかは気にしないと仮定します (たとえば、スレッドが互いに上書きしても問題なく、マップの値が使用された場合に発生する可能性のある競合状態については気にしません)。自体をロックします)。

例:

public class MyMap<K,V> extends HashMap<K,V> {
  //...
   public synchronized void put(K key, V value) {
      //...
   }

  //...
}

私の知る限り、HashMap は put メソッドでサイズ変更を行います。put はマップ インスタンス レベルで同期されるため、同時サイズ変更中に発生する問題は (おそらく) 発生しません。

上記の疑わしい仮定があっても、私の直感では、さらに問題が発生する可能性があることを示しています。それとも私はただ妄想的ですか?

更新: 皆さん、ありがとうございました。楽しくて啓発的でした。この特定のクラスの著者のオリジナルに会うことがあれば、彼の愚かさを詳細に説明することができます. :)

要約すると、 putAll は依然としてデータ構造をひどく台無しにし、恐ろしい無限ループ/データ競合状態に陥る可能性があります。get は、ハッシュマップの基になる内部データ構造に依存しており、同時に変更されている可能性があり、get プロセスが奇妙な動作をする原因となります。これは一般的に悪い考えです。少なくとも、作成者は代わりに Collections.synchronizedMap(Map) を使用できたはずです。

注: この記事の執筆時点で与えられた 3 つの回答はすべて実際には正しいですが、get() に関するものを正解として選びました。

4

3 に答える 3

5

putAllと も同期していることを願っていますremoveputAll特に、複数のスレッドが HashMap のサイズを変更しようとする可能性があるためです。これらのメソッドも更新さsizemodCount、同期外で行われると更新が失われる可能性があります。

于 2012-09-14T18:35:00.770 に答える
3

get()変化するデータ構造を読み取ることができるため、すべての悪いことが起こる可能性があります。

get() がデッド ループに陥っているのを見たことがあるので、これは単なる理論上の可能性ではなく、悪いことが起こります。

于 2012-09-14T18:33:57.553 に答える
2

コメントで述べたように、発生する可能性のある別の問題は、putAll(Map)メソッドが同期されていないように見えることです。Map の構造を変更することもできるためputAll、別のスレッドが同じ Map を使用しているときに、あるスレッドから非同期で呼び出すのは安全ではありません。

ただし、より高いレベルでは、なぜなぜput(key, value)だったのかをもっと理解することは興味深いでしょうsynchronized。マップの構造への同期されていない変更を防止したとしても、複数のスレッドが同期せずにマップにアクセスすることはまだ良い考えではないようです。実際、スレッド A が HashMap のコンテンツを繰り返し処理しようとしていて、スレッド B が を呼び出した場合、スレッド A のイテレータは依然として高速に失敗し、非決定論的なことを行うのではなくsynchronized put(key, value)a をスローします。ConcurrentModificationException

呼び出しを同期したとしてもputAll(Map)、マップのコンテンツを繰り返し処理している他のスレッドでは例外が発生します。Map を複数のスレッドで使用する必要があり、それらのスレッドの少なくとも 1 つが Map を変更する必要がある場合、すべての呼び出しを同期する必要があります。

于 2012-09-14T18:57:43.997 に答える