SynchronizedMap
とHashMap
でラッパークラスConcurrentHashMap
を使用することの違いは何ですか? HashMap
を繰り返しながら変更することはできますか(ConcurrentHashMap
)?
同期HashMap
:
各メソッドは、オブジェクトレベルのロックを使用して同期されます。したがって、synchMapのgetおよびputメソッドはロックを取得します。
コレクション全体のロックは、パフォーマンスのオーバーヘッドです。 1つのスレッドがロックを保持している間、他のスレッドはコレクションを使用できません。
ConcurrentHashMap
はJDK 5で導入されました。
オブジェクトレベルでのロックはありません。ロックは非常に細かい粒度で行われます。 ConcurrentHashMap
の場合、ロックはハッシュマップバケットレベルにあります。
下位レベルのロックの効果は、同期されたコレクションでは不可能なリーダーとライターを同時に使用できることです。これにより、スケーラビリティが大幅に向上します。
ConcurrentHashMap
は、1つのスレッドがそれを変更しようとしているときに別のスレッドがそれを繰り返している場合、ConcurrentModificationException
をスローしません。
この記事 Java 7:HashMap vs ConcurrentHashMap は非常に良い読み物です。強くお勧めします。
短い答え:
両方のマップは、Map
インターフェイスのスレッドセーフな実装です。 ConcurrentHashMap
は、高い同時実行性が予想される場合にスループットを高めるために実装されています。
ブライアンゲッツの 記事ConcurrentHashMap
の背後にある考え方に関する非常に良い読み物です。強くお勧めします。
ConcurrentHashMap
は、マップ全体を同期せずにスレッドセーフです。ロックを使用して書き込みが行われている間、読み取りは非常に高速に行われます。
ConcurrentHashMapとsynchronisedHashmapの両方を使用して、スレッドセーフを実現できます。しかし、そのアーキテクチャを見ると、多くの違いがあります。
オブジェクトレベルでロックを維持します。したがって、put/getなどの操作を実行する場合は、最初にロックを取得する必要があります。同時に、他のスレッドは操作を実行できません。そのため、一度に操作できるスレッドは1つだけです。そのため、ここで待機時間が長くなります。 ConcurrentHashMapと比較すると、パフォーマンスは比較的低いと言えます。
セグメントレベルでロックを維持します。 16個のセグメントがあり、デフォルトで並行性レベルを16に維持します。そのため、一度に16個のスレッドがConcurrentHashMapを操作できます。さらに、読み取り操作にはロックは必要ありません。したがって、任意の数のスレッドがget操作を実行できます。
Thread1がセグメント2でput操作を実行し、thread2がセグメント4でput操作を実行する場合、ここで許可されます。つまり、16スレッドが一度にConcurrentHashMapでupdate(put/delete)操作を実行できます。
待ち時間が短くなるように。したがって、パフォーマンスはsynchronisedHashmapよりも比較的優れています。
どちらもHashMapの同期バージョンであり、コア機能と内部構造が異なります。
ConcurrentHashMapは、概念的に独立したHashMapとして表示できる内部セグメントで構成されています。このようなセグメントはすべて、高度な同時実行で個別のスレッドによってロックできます。そのため、複数のスレッドは、互いにブロック/待機することなく、ConcurrentHashMapからキーと値のペアを取得/配置できます。これは、スループットを高めるために実装されています。
一方、
Collections.synchronizedMap()、HashMapの同期バージョンを取得し、ブロック方式でアクセスします。これは、複数のスレッドがsynchronizedMapに同時にアクセスしようとする場合、キーと値のペアを同期方式で一度に1つずつ取得/配置できることを意味します。
ConcurrentHashMap
は、lock stripping
として知られるきめの細かいロックメカニズムを使用して、より高度な共有アクセスを許可します。このため、より優れた同時実行およびスケーラビリティを提供します。
また、ConcurrentHashMap
に対して返されるイテレータは、フェイルファーストテクニックではなく弱一貫性であり、Synchronized HashMapで使用されます。
SynchronizedMap
のメソッドはオブジェクトのロックを保持しますが、ConcurrentHashMap
には、代わりにコンテンツのバケットにロックが保持される「ロックストライピング」の概念があります。したがって、スケーラビリティとパフォーマンスが向上しました。
ConcurrentHashMap:
1)両方のマップは、Mapインターフェースのスレッドセーフな実装です。
2)ConcurrentHashMapは、高い並行性が期待される場合に、より高いスループットのために実装されます。
3)オブジェクトレベルでのロックはありません。
同期ハッシュマップ:
1)各メソッドは、オブジェクトレベルのロックを使用して同期されます。
ConcurrentHashMapとSynchronized HashMapの簡単なパフォーマンステスト 。テストフローは、1つのスレッドでput
を呼び出し、get
の3つのスレッドでMap
を同時に呼び出します。 @trshivが言ったように、ConcurrentHashMapは、ロックなしの読み取り操作に対して、より高いスループットと速度を持っています。結果は、操作時間が10^7
を超えると、ConcurrentHashMapはSynchronized HashMapよりも2x
速くなります。
Javaのドキュメントに従って
HashtableとCollections.synchronizedMap(new HashMap())は同期されます。ただし、ConcurrentHashMapは「並行」です。
並行コレクションはスレッドセーフですが、単一の排他ロックによって管理されません。
ConcurrentHashMapの特定のケースでは、任意の数の同時読み取りおよび調整可能な数の同時書き込みを安全に許可します。 「同期化された」クラスは、単一のロックを介したコレクションへのすべてのアクセスを防止する必要がある場合に役立ちますが、スケーラビリティは低下します。
複数のスレッドが共通のコレクションにアクセスすることが予想されるその他の場合、通常は「同時」バージョンが推奨されます。また、同期されていないコレクションは、いずれかのコレクションが共有されていない場合、または他のロックを保持している場合にのみアクセスできる場合に適しています。
ConcurrentHashMapは、データへの同時アクセスを許可します。マップ全体はセグメントに分割されます。
読み取り操作get(Object key)
は、セグメントレベルでも同期されません。
しかし、書き込み操作、すなわち。 remove(Object key), get(Object key)
セグメントレベルでロックを取得します。マップ全体の一部のみがロックされ、他のスレッドはロックされたセグメントを除くさまざまなセグメントから値を読み取ることができます。
SynchronizedMap一方、オブジェクトレベルでロックを取得します。すべてのスレッドは、操作(読み取り/書き込み)に関係なく、現在のスレッドを待機する必要があります。