ConcurrentMapの分析と思考
4352 ワード
予備知識:Java HashMap and HashSetの実現メカニズム
hashmapのストレージ構造は、次のように準備されていることがわかります.
(画像はhttp://www.ibm.com/developerworks/cn/java/j-lo-hash/から)
つまり、hashmapの内部にはEntityクラス行の配列が含まれており、この配列の要素はEntityです.実際にmapに入れたkeyとvalueは、key、value、hashcode(key)、およびEntityの参照を含むEntityオブジェクトに対応しています.この参照により、Entityはチェーンテーブルを形成することができます.図では、青色の長方形のボックスは配列を表し、オレンジ色の楕円はEntityオブジェクトを表します.
HashMapクラスはスレッドが安全ではないことに注意してください.
ConcurrentMapの主なサブクラスはConcurrentHashMapです.
原理:1つのConcurrentHashMapは複数のsegmentからなり、各segmentは1つのEntityの配列を含む.ここではHashMapよりも1つのsegmentクラスが増えています.このクラスはReentrantLockクラスを継承しているので、それ自体がロックです.マルチスレッドがConcurrentHashMapに対して動作する場合、mapを完全にロックするのではなく、対応するsegmentをロックします.これにより、同時効率が向上します.
コンストラクション関数の解析:
これはConcurrentHashMapの無パラメトリック構造関数で、デフォルトのサイズは16、負荷係数は0.75、同時レベルは16であることがわかります.
Put関数の解析:
hash()関数によりkeyのハッシュ値が得られ,対応するsegmentが得られ,segmentによりEntityが格納されることが分かる.
同時に、「検出再修正」(条件付きスレッドセキュリティ[2]参照)などの同時問題を回避するために、ConcurrentHashMapはputIfAbsent(K key,V value)メソッドを提供し、keyが存在しない場合に追加します.(keyの存在は2つの条件に依存し,1つはkeyのhashcode法,もう1つはkeyのequal法である)
利点:map全体をロックするのではなく、対応するsegmentにロックをかけることで、同時性が向上します.挿入、検索および除去操作の伸縮性を直接向上させることができる.
欠点:mapの要素を巡回する場合、すべてのsegmentのロックを取得する必要があります.巡回を使用するのは遅いです.ロックが増加し、システムのリソースを占有しています.セット全体を動作させるいくつかの方法、例えば
hashmapのストレージ構造は、次のように準備されていることがわかります.
(画像はhttp://www.ibm.com/developerworks/cn/java/j-lo-hash/から)
つまり、hashmapの内部にはEntityクラス行の配列が含まれており、この配列の要素はEntityです.実際にmapに入れたkeyとvalueは、key、value、hashcode(key)、およびEntityの参照を含むEntityオブジェクトに対応しています.この参照により、Entityはチェーンテーブルを形成することができます.図では、青色の長方形のボックスは配列を表し、オレンジ色の楕円はEntityオブジェクトを表します.
HashMapクラスはスレッドが安全ではないことに注意してください.
ConcurrentMapの主なサブクラスはConcurrentHashMapです.
原理:1つのConcurrentHashMapは複数のsegmentからなり、各segmentは1つのEntityの配列を含む.ここではHashMapよりも1つのsegmentクラスが増えています.このクラスはReentrantLockクラスを継承しているので、それ自体がロックです.マルチスレッドがConcurrentHashMapに対して動作する場合、mapを完全にロックするのではなく、対応するsegmentをロックします.これにより、同時効率が向上します.
コンストラクション関数の解析:
/**
* Creates a new, empty map with a default initial capacity (16),
* load factor (0.75) and concurrencyLevel (16).
*/
public ConcurrentHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
これはConcurrentHashMapの無パラメトリック構造関数で、デフォルトのサイズは16、負荷係数は0.75、同時レベルは16であることがわかります.
Put関数の解析:
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
*
* <p> The value can be retrieved by calling the <tt>get</tt> method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, false);
}
hash()関数によりkeyのハッシュ値が得られ,対応するsegmentが得られ,segmentによりEntityが格納されることが分かる.
同時に、「検出再修正」(条件付きスレッドセキュリティ[2]参照)などの同時問題を回避するために、ConcurrentHashMapはputIfAbsent(K key,V value)メソッドを提供し、keyが存在しない場合に追加します.(keyの存在は2つの条件に依存し,1つはkeyのhashcode法,もう1つはkeyのequal法である)
利点:map全体をロックするのではなく、対応するsegmentにロックをかけることで、同時性が向上します.挿入、検索および除去操作の伸縮性を直接向上させることができる.
欠点:mapの要素を巡回する場合、すべてのsegmentのロックを取得する必要があります.巡回を使用するのは遅いです.ロックが増加し、システムのリソースを占有しています.セット全体を動作させるいくつかの方法、例えば
size()
またはisEmpty()
の実装をより困難にするのは、これらの方法は、一度に多くのロックを得ることが要求され、また、不正確な結果を返すリスクがあるからである.