1. 程式人生 > >如何線程安全的使用HashMap

如何線程安全的使用HashMap

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