web-dev-qa-db-ja.com

ハッシュマップの並行性の問題

速度上の理由から、ロックをオンにする必要がないハッシュマップがあります。古いデータを気にしないと仮定すると、更新とアクセスを同時に行うと問題が発生しますか?

私のアクセスは取得であり、反復ではなく、削除は更新の一部です。

30
Nathaniel Flath

はい、それは大きな問題を引き起こします。 1つの例は、ハッシュマップに値を追加するときに発生する可能性があることです。これにより、テーブルの再ハッシュが発生する可能性があり、別のスレッドが衝突リスト(ハッシュテーブルの「バケット」)を反復処理しているときに発生すると、そのスレッドが誤って発生する可能性があります。マップに存在するキーが見つかりません。 HashMapは、同時使用に対して明示的に安全ではありません。

代わりに ConcurrentHashMap を使用してください。

56
erickson

同期または使用の重要性 ConcurrentHashMap は控えめに言うことはできません。

数年前まで、HashMapのput操作とremove操作を同期するだけで解決できるという誤った印象を受けていました。もちろんこれは非常に危険であり、実際には一部の(1.5初期の)jdkでHashMap.get()に無限ループが発生します。

私が数年前にしたこと(そして実際に行うべきではない):

public MyCache {
    private Map<String,Object> map = new HashMap<String,Object>();

    public synchronzied put(String key, Object value){
        map.put(key,value);
    }

    public Object get(String key){
        // can cause in an infinite loop in some JDKs!!
        return map.get(key);
    }
}

[〜#〜]編集[〜#〜]しないの例を追加すると思いました(を参照)上記)

16
Gareth Davis

疑わしい場合は、クラスの Javadocs を確認してください。

この実装は同期されていないことに注意してください。複数のスレッドが同時にハッシュマップにアクセスし、少なくとも1つのスレッドがマップを構造的に変更する場合外部で同期する必要があります。 (構造変更とは、1つ以上のマッピングを追加または削除する操作です。インスタンスに既に含まれているキーに関連付けられた値を変更するだけでは、構造変更にはなりません。)これは通常、マップを自然にカプセル化するオブジェクトで同期することによって実現されます。 。そのようなオブジェクトが存在しない場合は、Collections.synchronizedMapメソッドを使用してマップを「ラップ」する必要があります。これは、マップへの偶発的な非同期アクセスを防ぐために、作成時に行うのが最適です。

Map m = Collections.synchronizedMap(new HashMap(...));

(強調は私のものではありません)

したがって、スレッドがマップからマッピングを削除すると言ったという事実に基づいて、答えはyes間違いなく問題を引き起こし、そうです間違いなく安全ではありません

12
matt b

はい。 非常に悪いこと 起こります。たとえば、スレッドが無限ループでスタックする可能性があります。

ConcurrentHashMap 、または NonBlockingHashMap のいずれかを使用します

10
ykaganovich

あなたが説明する条件はHashMapによって満たされません。マップを更新するプロセスはアトミックではないため、無効な状態のマップが発生する可能性があります。複数の書き込みを行うと、破損した状態になる可能性があります。 ConcurrentHashMap (1.5以降)はあなたが望むことをします。

7
Kathy Van Stone

「同時に」が複数のスレッドからのものである場合は、そのアクセスをロックする必要があります(または、ロックを行うConcurrentHashMapなどを使用します)。

4
nos

いいえ、次のことを行っても問題はありません。

  1. マルチスレッドが発生する前に、シングルスレッドの最初のロード時にデータをHashMapに配置します。これは、データを追加するプロセスがmodcountを変更し、最初に追加したとき(nullが返される)とデータを置き換えるとき(古いデータは返されますが、modcountは変更されない)で異なるためです。 Modcountは、イテレータをフェイルファストにするものです。ただし、getを使用している場合は、何も繰り返されないため、問題ありません。

  2. アプリケーション全体で同じキーを使用します。アプリケーションが起動してデータをロードすると、他のキーをこのマップに割り当てることはできません。このようにして、getは古いデータまたは新しく挿入されたデータのいずれかを取得します-問題はありません。

0
MetroidFan2002

私はここか他の場所で読みました、いいえ、あなたはマルチスレッドからアクセスしません、しかし誰も実際に何が起こっているのかを言いません。

したがって、3月から本番環境で実行されているアプリケーションで今日(これが古い質問になっている理由です):2を同じHashSet(次にHashMap)に置くと、CPUの過負荷(ほぼ100%)が発生し、メモリが増加します3GBの、そしてGCによってダウン。アプリを再起動する必要があります。

0
C.Twins

他の言及されているように、ConcurrentHashMapを使用するか、マップを更新するときにマップを同期します。

0
John Doe