Map實現執行緒安全的3種方式

weixin_34214500發表於2019-03-07

我們常用的HashMap不是執行緒安全的,如果要實現執行緒安全有3種方式,分別是使用HashTable,使用Colloections的SynchronizedMap方法,使用ConcurrentHashMap

1. HashTable

Map<String, Object> map = new Hashtable<>();
來看看HashTable的原始碼。

public synchronized V put(K key, V value) {
      ...
}

public synchronized V get(Object key) {
      ...
}

HashTable的get/put方法都被synchronized關鍵字修飾,說明它們是方法級別阻塞的,它們佔用共享資源鎖,所以導致同時只能一個執行緒操作get或者put,而且get/put操作不能同時執行,所以這種同步的集合效率非常低,一般不建議使用這個集合。

2. SynchronizedMap

Map<String, Object> map = Collections.synchronizedMap(new HashMap<String, Object>());
這種是直接使用工具類裡面的方法建立SynchronizedMap,把傳入進行的HashMap物件進行了包裝同步而已,來看看它的原始碼。

private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        
        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        ...
}

SynchronizedMap的實現方式是加了個物件鎖,每次對HashMap的操作都要先獲取這個mutex的物件鎖才能進入,所以效能也不會比HashTable好到哪裡去,也不建議使用。

3. ConcurrentHashMap

Map<String, Object> map = new ConcurrentHashMap<>();
這個也是最推薦使用的執行緒安全的Map,也是實現方式最複雜的一個集合,每個版本的實現方式也不一樣,在jdk8之前是使用分段加鎖的一個方式,分成16個桶,每次只加鎖其中一個桶,而在jdk8又加入了紅黑樹和CAS演算法來實現。

雖然實現起來很複雜,但使用起來也是非常簡單的,在java面試中問的頻率也非常高,最重要的是效能要比上面兩種同步方式要快太多,推薦使用。

參考:面試必問-幾種執行緒安全的Map解析

相關文章