HashMapはさまざまなキーに対してスレッドセーフですか?


回答:


99

@dotsidの回答で、彼は次のように述べています。

何らかの方法でHashMapを変更すると、コードが壊れてしまいます。

彼は正しい。同期なしで更新されたHashMapは、スレッドが互いに素なキーのセットを使用している場合でも壊れます。うまくいかないことがいくつかあります。

  • あるスレッドがを実行するput場合、別のスレッドはハッシュマップのサイズの古い値を見る可能性があります。

  • スレッドがputテーブルの再構築をトリガーするaを実行すると、別のスレッドがハッシュテーブル配列参照、そのサイズ、その内容、またはハッシュチェーンの一時的または古いバージョンを確認する場合があります。混沌が続く可能性があります。

  • スレッドがput他のスレッドで使用されているキーと衝突するキーに対してaを実行し、後者のスレッドがputそのキーに対してaを実行すると、後者はハッシュチェーン参照の古いコピーを見る可能性があります。混沌が続く可能性があります。

  • あるスレッドが他のスレッドのキーの1つと衝突するキーでテーブルをプローブすると、チェーン上でそのキーに遭遇する可能性があります。そのキーでequalsを呼び出し、スレッドが同期されていない場合、equalsメソッドはそのキーで古い状態に遭遇する可能性があります。

また、2つのスレッドが同時に実行putまたはremove要求している場合、競合状態になる可能性が多数あります。

私は3つの解決策を考えることができます:

  1. を使用しConcurrentHashMapます。
  2. 通常を使用しますHashMapが、外部で同期します。たとえば、プリミティブミューテックス、Lockオブジェクトなどを使用します。
  3. HashMapスレッドごとに異なるものを使用してください。スレッドに実際に互いに素なキーのセットがある場合は、(アルゴリズムの観点から)単一のマップを共有する必要はありません。実際、アルゴリズムに、ある時点でマップのキー、値、またはエントリを反復するスレッドが含まれる場合、単一のマップを複数のマップに分割すると、処理のその部分が大幅に高速化される可能性があります。

30

ConcurrentHashMapを使用するだけです。ConcurrentHashMapは、ハッシュバケットの範囲をカバーする複数のロックを使用して、ロックが競合する可能性を減らします。競合していないロックを取得すると、パフォーマンスにわずかな影響があります。

元の質問に答えるには:javadocによると、マップの構造が変更されない限り、問題はありません。これは、要素をまったく削除せず、マップにまだ存在しない新しいキーを追加しないことを意味します。既存のキーに関連付けられている値を置き換えることは問題ありません。

複数のスレッドが同時にハッシュマップにアクセスし、少なくとも1つのスレッドがマップを構造的に変更する場合は、外部で同期する必要があります。(構造変更とは、1つ以上のマッピングを追加または削除する操作です。インスタンスに既に含まれているキーに関連付けられた値を変更するだけでは、構造変更にはなりません。)

視認性を保証するものではありませんが。したがって、古い関連付けの取得をときどき受け入れる必要があります。


6

それはあなたが「アクセスする」の下で何を意味するかによります。読んでいるだけなら、「起こる前に」ルールで保証されたデータの可視性があれば、同じキーでも読むことができます。これは、HashMap変更してはならず、すべての変更(初期構築)は、リーダーがにアクセスし始める前に完了する必要があることを意味しHashMapます。

HashMap何らかの方法でを変更すると、コードは単に壊れます。@Stephen Cは、その理由を非常によく説明しています。

編集:最初のケースが実際の状況である場合はCollections.unmodifiableMap()、HashMapが決して変更されないことを確認するために使用することをお勧めします。が指すオブジェクトHashMapも変更しないでfinalください。キーワードを積極的に使用すると役立ちます。

そして、@ Lars Andrenが言うように、ConcurrentHashMapほとんどの場合、最良の選択です。


2
私の意見では、ConcurrentHashMapが最良の選択です。著者が質問しなかったため、私がそれを推奨しなかった唯一の理由:) CAS操作のためにスループットが低下しますが、並行プログラミングの黄金律が言うように、「正しくして、それから速くしてください。 ":)
Denis Bazhenov 2010

unmodifiableMapクライアントがマップを変更できないようにします。基になるマップが変更されていないことを保証するものではありません。
ピートカーカム2010

すでに指摘したように、「HashMapによってポイントされるオブジェクトも変更されるべきではありません」
Denis Bazhenov 2010

4

2つのスレッドから適切に同期せずにHashMapを変更すると、競合状態が発生しやすくなります。

  • put()が内部テーブルのサイズ変更につながる場合、これには時間がかかり、他のスレッドは古いテーブルへの書き込みを続行します。
  • put()キーのハッシュコードがテーブルサイズを法として等しい場合、異なるキーの2つは同じバケットの更新につながります。(実際には、ハッシュコードとバケットインデックスの関係はより複雑ですが、それでも衝突が発生する可能性があります。)

1
競合状態よりも悪いです。使用しているHashMap実装の内部によっては、HashMapメモリの異常によってデータ構造などが破損する可能性があります。
スティーブンC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.