1. 程式人生 > >線程安全的HashMap

線程安全的HashMap

16px pan req tex 偶數 stat 實現 常見 也會

 一、一般模式下線程安全的HashMap

  默認情況常用的HashMap都是線程不安全的,在多線程的環境下使用,常常會造成不可預知的,莫名其妙的錯誤。那麽,我們如何實現一個線程安全的HashMap呢?其中一個可行的方式是使用Collectons.synchronizedMap() 方法來包裝我們的HashMap。如下:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String,String>());

  

Collections.synchronizedMap()會生成一個SynchronizedMap,它使用委托模式,將自己HashMap相關的功能交給傳入HashMap實現,二自己負責線程安全的相關實現,下面看看
SynchronizedMap的定義:
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map

        // 使用 mutex 實心對 map 的互斥操作
        final
Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { this.m = Objects.requireNonNull(m); mutex = this; }

  如在代碼中看到的,所有對Map的操作都需要用 這個 mutex 來同步,以實現線程安全。比如說下面這些常見的對HashMap的操作方法:

        public boolean containsKey(Object key) {
            
synchronized (mutex) {return m.containsKey(key);} } public boolean containsValue(Object value) { synchronized (mutex) {return m.containsValue(value);} } public V get(Object key) { synchronized (mutex) {return m.get(key);} }

  除了以上看到的方法之外,其他的Map相關的方法有類似的操作。雖然這個包裝的Map可以實現線程安全的要求,但是,它在多線程環境下的性能表現並不是很好,無論是對Map的讀取還是寫入,偶數需要獲得 mutex 的同步鎖,這會導致所有對Map的安全操作也會進入等待狀態,知道mutex可用。 如果並發級別不高,那麽這個 包裝的Map可以基本滿足要求,但是在搞並發的環境中,我們需要尋找新的解決方案。 ——---> 那就是我們的 ConcurrentHashMap.

二、提高"鎖"性能的策略

  1. 減少鎖的持有時間

    只在必要時進行同步,減少鎖的持有時間。比如說在一個方法中只有一個變量需要同步,那麽就沒有必要對這整個方法都進行同步,而只需要同步這個變量即可。

        // 無謂的加鎖時間
        public synchronied void syncMethod() {

                othrerMethod();
                mutexMethod();
                otherMethod();
        }

        // 正確的加鎖時間
        public  void syncMethod() {

                othrerMethod();
                synchronied(this){
                mutexMethod();
                }
                otherMethod();
        }

  2. 減小鎖的粒度

    在獲取全局信息方法不頻繁的時候,通過減小鎖的粒度可以搞系統的吞吐量。

  3. 讀寫分離鎖替換獨占鎖

    在讀都寫少的情況下,使用讀寫分離鎖,多線程讀時不阻塞,而只對寫線程進行同步。

  4. 鎖分離

    對不同功能的鎖進行不同的鎖策略。

  5. 鎖粗化

    系統對於"鎖"的調度也是需要性能消耗的,又是我們可以適當的加大鎖的範圍,比如說在循環中盡量減少對鎖的請求和釋放,而是在得到鎖的情況,一次性把問題解決。

  

線程安全的HashMap