Collections.synchronizedMap()、ConcurrentHashMap、Hashtable之間的區別


為什么要比較Hashtable、SynchronizedMap()、ConcurrentHashMap之間的關系?因為常用的HashMap是非線程安全的,不能滿足在多線程高並發場景下的需求。

那么為什么說HashTable是線程不安全的?具體參閱關於java集合類HashMap的理解

如何線程安全的使用HashMap

了解了 HashMap 為什么線程不安全,那現在看看如何線程安全的使用 HashMap。這個無非就是以下三種方式:

  • Hashtable
  • ConcurrentHashMap
  • Synchronized Map

 

Hashtable

那先說說Hashtable,Hashtable源碼中是使用 synchronized 來保證線程安全的,比如下面的 get 方法和 put 方法:

public synchronized V get(Object key) {
       // 省略實現
}
public synchronized V put(K key, V value) {
    // 省略實現
}

所以當一個線程訪問 HashTable 的同步方法時,其他線程如果也要訪問同步方法,會被阻塞住。舉個例子,當一個線程使用 put 方法時,另一個線程不但不可以使用 put 方法,連 get 方法都不可以,好霸道啊!!!so~~,效率很低,現在基本不會選擇它了。

Collections.synchronizedMap()

看了一下源碼,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 的操作是線程安全的。

ConcurrentHashMap

Spring的源碼中有很多使用ConcurrentHashMap的地方。具體參閱》》》》》》》》。需要注意的是,上面博客是基於 Java 7 的,和8有區別,在8中 CHM 摒棄了 Segment(鎖段)的概念,而是啟用了一種全新的方式實現,利用CAS算法。

下面通過一個具體例子看看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花費的時間比前兩個的一半還少。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM