爲何要比較Hashtable、SynchronizedMap()、ConcurrentHashMap之間的關係?由於經常使用的HashMap是非線程安全的,不能知足在多線程高併發場景下的需求。html
那麼爲何說HashTable是線程不安全的?具體參閱關於java集合類HashMap的理解java
瞭解了 HashMap 爲何線程不安全,那如今看看如何線程安全的使用 HashMap。這個無非就是如下三種方式:算法
那先說說Hashtable,Hashtable源碼中是使用 synchronized
來保證線程安全的,好比下面的 get 方法和 put 方法:安全
public synchronized V get(Object key) { // 省略實現 } public synchronized V put(K key, V value) { // 省略實現 }
因此當一個線程訪問 HashTable 的同步方法時,其餘線程若是也要訪問同步方法,會被阻塞住。舉個例子,當一個線程使用 put 方法時,另外一個線程不但不可使用 put 方法,連 get 方法都不能夠,好霸道啊!!!so~~,效率很低,如今基本不會選擇它了。多線程
看了一下源碼,synchronizedMap()的實現仍是很簡單的。併發
1 // synchronizedMap方法 2 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { 3 return new SynchronizedMap<>(m); 4 } 5 // SynchronizedMap類 6 private static class SynchronizedMap<K,V> 7 implements Map<K,V>, Serializable { 8 private static final long serialVersionUID = 1978198479659022715L; 9 10 private final Map<K,V> m; // Backing Map 11 final Object mutex; // Object on which to synchronize 12 13 SynchronizedMap(Map<K,V> m) { 14 this.m = Objects.requireNonNull(m); 15 mutex = this; 16 } 17 18 SynchronizedMap(Map<K,V> m, Object mutex) { 19 this.m = m; 20 this.mutex = mutex; 21 } 22 23 public int size() { 24 synchronized (mutex) {return m.size();} 25 } 26 public boolean isEmpty() { 27 synchronized (mutex) {return m.isEmpty();} 28 } 29 public boolean containsKey(Object key) { 30 synchronized (mutex) {return m.containsKey(key);} 31 } 32 public boolean containsValue(Object value) { 33 synchronized (mutex) {return m.containsValue(value);} 34 } 35 public V get(Object key) { 36 synchronized (mutex) {return m.get(key);} 37 } 38 39 public V put(K key, V value) { 40 synchronized (mutex) {return m.put(key, value);} 41 } 42 public V remove(Object key) { 43 synchronized (mutex) {return m.remove(key);} 44 } 45 // 省略其餘方法 46 }
從源碼中能夠看出調用 synchronizedMap() 方法後會返回一個 SynchronizedMap 類的對象,而在 SynchronizedMap 類中使用了 synchronized 同步關鍵字來保證對 Map 的操做是線程安全的。dom
Spring的源碼中有不少使用ConcurrentHashMap的地方。具體參閱》》》》》》》》。須要注意的是,上面博客是基於 Java 7 的,和8有區別,在8中 CHM 摒棄了 Segment(鎖段)的概念,而是啓用了一種全新的方式實現,利用CAS算法。ide
下面經過一個具體例子看看Collections.synchronizedMap()和ConcurrentHashMap哪一個性能更高。高併發
1 public class Test { 2 3 public final static int THREAD_POOL_SIZE = 5; 4 5 public static Map<String, Integer> crunchifyHashTableObject = null; 6 public static Map<String, Integer> crunchifySynchronizedMapObject = null; 7 public static Map<String, Integer> crunchifyConcurrentHashMapObject = null; 8 9 public static void main(String[] args) throws InterruptedException { 10 11 // Test with Hashtable Object 12 crunchifyHashTableObject = new Hashtable<>(); 13 crunchifyPerformTest(crunchifyHashTableObject); 14 15 // Test with synchronizedMap Object 16 crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>()); 17 crunchifyPerformTest(crunchifySynchronizedMapObject); 18 19 // Test with ConcurrentHashMap Object 20 crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>(); 21 crunchifyPerformTest(crunchifyConcurrentHashMapObject); 22 23 } 24 25 public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException { 26 27 System.out.println("Test started for: " + crunchifyThreads.getClass()); 28 long averageTime = 0; 29 for (int i = 0; i < 5; i++) { 30 31 long startTime = System.nanoTime(); 32 ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE); 33 34 for (int j = 0; j < THREAD_POOL_SIZE; j++) { 35 crunchifyExServer.execute(new Runnable() { 36 @SuppressWarnings("unused") 37 @Override 38 public void run() { 39 40 for (int i = 0; i < 500000; i++) { 41 Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000); 42 43 // Retrieve value. We are not using it anywhere 44 Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber)); 45 46 // Put value 47 crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber); 48 } 49 } 50 }); 51 } 52 53 // Make sure executor stops 54 crunchifyExServer.shutdown(); 55 56 // Blocks until all tasks have completed execution after a shutdown request 57 crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); 58 59 long entTime = System.nanoTime(); 60 long totalTime = (entTime - startTime) / 1000000L; 61 averageTime += totalTime; 62 System.out.println("2500K entried added/retrieved in " + totalTime + " ms"); 63 } 64 System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n"); 65 } 66 }
結果顯示,ConcurrentHashMap性能是明顯優於Hashtable和SynchronizedMap的,ConcurrentHashMap花費的時間比前兩個的一半還少。post