Collections.synchronizedMap()とConcurrentHashMapの違い
前述したCollections.synchronizedMap()とConcurrentHashMの両方がスレッド同期機能を提供しています.両者の違いはどこにあるのでしょうか.まずコードの例を見てみましょう.
次のコードは、あるスレッドがmapを書き込み、別のスレッドがmapデータを読み出して印刷することを実現する.
コードを実行すると、クラス内のオブジェクトmap値がmap 1,map 2,map 4の場合、プログラムはjava.util.C o n c u r r e ntModificationException異常を放出することがわかります.map=map 3の場合、コードは正常に動作します.
Collections.synchronizedMap()とConcurrentHashMapの主な違いは、Collections.synchronizedMap()とHashtableのように、実装上はmapのすべてのメソッドを呼び出すときにmap全体を同期させるが、ConcurrentHashMapの実装はより細かく、mapのすべてのバケツにロックをかけている.したがって、1つのスレッドがmapにアクセスする限り、他のスレッドはmapにアクセスできません.一方、1つのスレッドがConcurrentHashMapのバケツにアクセスしている場合、他のスレッドはmapに対していくつかの操作を実行できます.これにより、ConcurrentHashMapは、Collections.synchronizedMap()よりもパフォーマンスおよびセキュリティに優れています.同時に、同期操作はバケツに正確に制御されるので、mapを巡って他のスレッドがmapをデータ修正しようとしても、ConcurrentModificationExceptionは放出されません. しかし、注意深い友人は、上記の例では、map=map 3の場合でも、最後に印刷された結果は100行ではないことを発見したかもしれません.なぜなら、Collections.synchronizedMap()とConcurrentHashMapのmap同期に対する原子操作の両方が機能するmapの方法では、mapは読み取りとクリアの間でスレッド間で同期しないからである.上記のコードの不足は、これらの同期mapを信頼しすぎて、混合操作による影響を無視していることです.正しい方法はmapの読み取りとクリアを原子操作と見なし,コードブロック全体にロックを加えることである.
もう一つの違いは、ConcurrentHashMapはクラスの名前から、必然的にHashMapであることがわかります.一方、Collections.synchronizedMap()は、任意のMapインスタンスを受信し、Mapの同期を実現することができる.この例を見てください.
次のコードは、あるスレッドがmapを書き込み、別のスレッドがmapデータを読み出して印刷することを実現する.
package test.map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest2 {
private static Map map1 = new HashMap();
private static Map map2 = new Hashtable();
private static Map map3 = new ConcurrentHashMap();
private static Map map4 = Collections.synchronizedMap(new HashMap());
private static Map map = map4;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (map.size() > 0) {
for (Map.Entry entry : map.entrySet()) {
System.out.println(String.format("%s: %s", entry.getKey(), entry.getValue()));
}
map.clear();
}
try {
Thread.sleep((new Random().nextInt(10) + 1) * 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
map.put("key" + i, "value" + i);
try {
Thread.sleep((new Random().nextInt(10) + 1) * 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
コードを実行すると、クラス内のオブジェクトmap値がmap 1,map 2,map 4の場合、プログラムはjava.util.C o n c u r r e ntModificationException異常を放出することがわかります.map=map 3の場合、コードは正常に動作します.
Collections.synchronizedMap()とConcurrentHashMapの主な違いは、Collections.synchronizedMap()とHashtableのように、実装上はmapのすべてのメソッドを呼び出すときにmap全体を同期させるが、ConcurrentHashMapの実装はより細かく、mapのすべてのバケツにロックをかけている.したがって、1つのスレッドがmapにアクセスする限り、他のスレッドはmapにアクセスできません.一方、1つのスレッドがConcurrentHashMapのバケツにアクセスしている場合、他のスレッドはmapに対していくつかの操作を実行できます.これにより、ConcurrentHashMapは、Collections.synchronizedMap()よりもパフォーマンスおよびセキュリティに優れています.同時に、同期操作はバケツに正確に制御されるので、mapを巡って他のスレッドがmapをデータ修正しようとしても、ConcurrentModificationExceptionは放出されません. しかし、注意深い友人は、上記の例では、map=map 3の場合でも、最後に印刷された結果は100行ではないことを発見したかもしれません.なぜなら、Collections.synchronizedMap()とConcurrentHashMapのmap同期に対する原子操作の両方が機能するmapの方法では、mapは読み取りとクリアの間でスレッド間で同期しないからである.上記のコードの不足は、これらの同期mapを信頼しすぎて、混合操作による影響を無視していることです.正しい方法はmapの読み取りとクリアを原子操作と見なし,コードブロック全体にロックを加えることである.
もう一つの違いは、ConcurrentHashMapはクラスの名前から、必然的にHashMapであることがわかります.一方、Collections.synchronizedMap()は、任意のMapインスタンスを受信し、Mapの同期を実現することができる.この例を見てください.
package test.map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest4 {
private static void writeMap(Map map) {
for (int i = 0; i < 10; i++) {
map.put("key" + i, "value" + i);
}
}
private static void printMap(Map map) {
for (Map.Entry entry : map.entrySet()) {
System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}
}
public static void main(String[] args) {
Map map1 = new HashMap();
writeMap(map1);
printMap(map1);
System.out.println();
Map map2 = Collections.synchronizedMap(new TreeMap());
writeMap(map2);
printMap(map2);
System.out.println();
Map map3 = new ConcurrentHashMap();
writeMap(map3);
printMap(map3);
}
}
map 2はTreeMapであるため、最後に印刷された結果はKey値に従ってソートされ、map 3は明らかに結果の秩序を保証できない.