如何创建一个线程安全的Map?
1,使用普通的旧的Hashtable
HashMap允许null作为key,而Hashtable不可以
2,使用Collections中同步化的包装方法synchronizedMap
3,使用concurrent包下的ConcurrentHashMap
//Hashtable Example Code
Map<String, Integer> threadSafeMap = new Hashtable<String, Integer>();
//synchronizedMap Example Code.
threadSafeMap = Collections.synchronizedMap(new HashMap<String, Integer>());
//ConcurrentHashMap Example Code
threadSafeMap = new ConcurrentHashMap<String, Integer>();
threadSafeMap .put("Key1", 123);
ConcurrentHashMap 性能最好
HashMap线程不安全的体现
- resize死循环
- fail-fast
如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。
这个异常意在提醒开发者及早意识到线程安全问题,具体原因请查看ConcurrentModificationException的原因以及解决措施
测试三种并发集合的读写效率
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
//测试三种并发集合的读写效率
public class ConcurrentHashMapTest {
public static void main(String[] args) {
//HashMap不是线程安全的,通过 Collections.synchronizedMap()转换成线程安全的.
final Map<Integer, Integer> hm = Collections.synchronizedMap(new HashMap<Integer, Integer>());
//HashTable内部自带同步,线程安全的.
final Map<Integer, Integer> ht = new Hashtable<Integer, Integer>();
//JDK1.5之后提供的并发集合.
final Map<Integer, Integer> chm = new ConcurrentHashMap<Integer, Integer>();
putMap(hm);//
putMap(ht);//
putMap(chm);//数据量达到一定程度之后,会比前两种快3~4倍左右.
//TODO JDK1.8
// 8312
// 7053
// 6712
//TODO JDK1.7
// 8672
// 8332
// 6842
}
private static void putMap(final Map<Integer, Integer> hm) {
long begin = System.currentTimeMillis();
for (int k = 0; k < 100; k++) {//为了让效果更明显,再循环100次.
for (int i = 0; i < 1000; i++) {//1000条线程
final int key = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {//每条线程向其中添加1000次
hm.put(key, j);
}
}
}).start();
}
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
}