如何线程安全的使用HashMap
众所周知,HashMap是线程不安全的,不能在多线程高并发场景下使用。(具体原因→点击)
如何线程安全的使用HashMap?
- Collections.synchronizeMap(hashMap)
- ConcurrentHashMap
① Collections.synchronizedMap()
查看源码可知,在 SynchronizedMap 类中使用了 synchronized 同步关键字来保证对 Map 的操作是线程安全的。
②ConcurrentHashMap
Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
具体详解可参考(来源:优知学院):高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8)
下面测试一下HashTable、Collections.synchronizedMap()和ConcurrentHashMap的性能:
public class Main {
private static final Logger logger = Logger.getLogger(NewTest.class);
//初始化线程池大小为5
public final static int THREAD_POOL_SIZE = 5;
public static Map<String, Integer> tHashTable = null;
public static Map<String, Integer> tSynchronizedMap= null;
public static Map<String, Integer> tConcurrentHashMap = null;
public static void main(String[] args) throws Exception {
String paths[] = {"classpath*:/spring/root-context.xml", "classpath*:/spring/database-context.xml"};
ApplicationContext cxk = new ClassPathXmlApplicationContext(paths);
tHashTable = new Hashtable<>();
performanceTest(tHashTable);
tSynchronizedMap = Collections.synchronizedMap(new HashMap<String, Integer>());
performanceTest(tSynchronizedMap);
tConcurrentHashMap = new ConcurrentHashMap<>();
performanceTest(tConcurrentHashMap);
}
/**
* 多线程(5个),每个线程有5个线程池,每个线程池在5次循环下 共执行2500000次put操作
* @param tMap
* @throws InterruptedException
*/
public static void performanceTest(final Map<String, Integer> tMap) throws InterruptedException {
System.out.println(tMap.getClass()+"测试开始.........");
long averageTime = 0;
for (int i = 0; i < 5; i++) {
// System.nanoTime(): 返回系统计时器的当前值,以毫微秒为单位
// System.currentTimeMillis(): 当前时间与协调世界时 1970 年 1 月 1 日午夜之间的时间差(以毫秒为单位测量)
long startTime = System.nanoTime();
ExecutorService tExecutorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (int j = 0; j < THREAD_POOL_SIZE; j++) {
tExecutorService.execute(new Runnable() {
@SuppressWarnings("unused")
@Override
public void run() {
for (int k = 0; k < 500000; k++) {
Integer tRandomNumber = (int) Math.ceil(Math.random() * 1000000);
tMap.put(String.valueOf(tRandomNumber), tRandomNumber);
}
}
});
}
/**
*将线程池状态置为shutdown,并不会立即停止:
* 停止接收外部submit的任务
* 内部正在跑的任务和队列里等待的任务,会执行完
* 等到第二步完成后,才真正停止
*/
tExecutorService.shutdown();
/**
* 当前线程阻塞,直到
* 等所有已提交的任务(包括正在跑的和队列中等待的)执行完
* 或者等超时时间到
* 或者线程被中断,抛出InterruptedException
*/
tExecutorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
long entTime = System.nanoTime();
long spendTime = (entTime - startTime) / 1000000L;
averageTime += spendTime;
System.out.println("It takes " + spendTime + " ms"+" to execute 'put' 2.5 million times");
}
System.out.println(tMap.getClass() + "平均耗时" + averageTime / 5 + " ms\n");
}
}
比较测试结果可知:ConcurrentHashMap的效率还是比较乐观的;
虽然HashTable是线程安全的,但是HashTable线程安全的策略实现代价却太大了,简单粗暴,
get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁。
The more time goes by , The more life happens