Java多線程之同步類容器與併發容器

同步容器類

同步容器類包括Vector和HashTable,而且是早期JDK的一部分,這些同步的封裝器類是由Collections.synchronizedxxx等工廠方法創建的。例如
Map<String,String> map =Collections.synchronizedMap(new HashMap<String,String >());

HashMap本來不是線程安全的,但是上面的map就是線程安全的,

這裏寫圖片描述

在源碼中可以看到,map的方法都被synchronized修飾了。


同步容器類都是線程安全的,但在某些情況下可能需要額外的客戶端鎖來保護複合操作,比如:迭代以及條件運算,在同步類容器中,這些複合操作在沒有客戶端加鎖的情況下是仍然是線程安全的,但是當其他線程併發的修改容器時,他們可能會出現異常。

    public static Object getLat(Vector list){
        int lastIndex =list.size()-1;
        return list.get(lastIndex);
    }
    public static void deleteLast(Vector list){

        int lastIndex =list.size()-1;
        list.remove(lastIndex);
    }

上面的代碼,如果線程A在包含10個元素的Vector上調用getLast,同時線程B在同一個Vector上調用deleteLast,這時可能會拋出ArrayIndexOutOfBoundsException異常.
想要避免上面異常的出現,可以再迭代過程中持有容器的鎖

synchronized (vector){
    for(int i=0;i<vector.size();i++){
        doSomrthing(vector.get(i))
    }
}

然而在迭代期間對容器加鎖,並不是一個很好的方法,例如,某些線程在可以訪問容器之前,必須等待迭代過程結束,如果容器的規模很大,那麼這些線程就會長時間得到等待,這可能會產生死鎖,極大地降低了脫吐量和CPU的利用率。
同步類容器最大的缺點是不支持高併發!性能差!!


併發容器

Java5.0提供了多種併發容器類來改進同步容器的性能。同步類容器將所有對容器狀態的訪問都串行化,以實現它們的線程安全性。這種方法的代價是嚴重降低併發性!!!

Java5.0中增加了ConcurrentHashMap 用來代替同步且基於散列的Map,以及CopyOnWriteArrayList,CopyOnWriteArraySet 來代替同步的List和Set。

ConcurrentHashMap

同步類容器在執行每個操作的期間都持有一把鎖。當遍歷很長的鏈表並且在某些或者全部的元素上調用equals方法會花費很長的時間,而其他線程在此期間都不能訪問該容器。
與HashMap一樣,ConcurrentHashMap也是一個基於散列的Map,但是他使用了一種完全不同的加鎖機制來提供更高的併發性和伸縮性。ConcurrentHashMap使用的是一種粒度更細的加鎖機制來實現更大數據的共享,這種機制成爲分段鎖
在這種機制中,任意數量的線程可以併發的訪問容器。ConcurrentHashMap帶來的效果是在併發情況下實現更高的吞吐量,而在單線程環境下也只是損失非常小的性能。
ConcurrentHashMap提供的迭代器不是拋出ConcurrentModificationException異常。

CopyOnWriteArrayList

CopyOnWriteArrayList用來代替同步的List在某些情況下,它提供了更好的併發性能,並且在迭代期間不需要對容器進行加鎖或複製,
“寫入時複製(Copy-On-Write)”容器的線程安全性在於只要發佈一個正確的不可變得對象,那麼在訪問該對象時就不需要進一步的同步。

 /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //得到array的引用
            Object[] elements = getArray();
            int len = elements.length;
            //複製一個數組並且長度加一(添加元素)
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //保存新添加的元素
            newElements[len] = e;
            //將新數組對象賦值給array
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

容器中的array就是存儲元素的容器,

private transient volatile Object[] array;

CopyOnWriteArrayList適用於讀操作多寫操作少的場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章