如何線程安全的使用HashMap
阿新 • • 發佈:2019-05-08
pool tro down value 時間 put nan lims 系列
眾所周知,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的,這相當於給整個哈希表加了一把大鎖。
如何線程安全的使用HashMap