中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中ArrayList線程不安全的解決方法

發布時間:2021-08-30 14:16:15 來源:億速云 閱讀:231 作者:小新 欄目:開發技術

這篇文章主要介紹了Java中ArrayList線程不安全的解決方法,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

ArrayList線程不安全怎么辦?

有三種解決方法:

使用對應的 Vector 類,這個類中的所有方法都加上了 synchronized 關鍵字

  • 就和 HashMap 和 HashTable 的關系一樣

使用 Collections 提供的 synchronizedList 方法,將一個原本線程不安全的集合類轉換為線程安全的,使用方法如下:

List<Integer> list = Collections.synchronizedList(new ArrayList<>());

其實 HashMap 也可以用這招:

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

這個看上去有點東西,其實也是給每個方法加上一個 synchronized,不過不是直接加在方法上,而是加在方法內部,只有當線程獲取到 mutex 這個對象的鎖,才能進入代碼塊:

public E get(int index) {
    synchronized (mutex) {
        return list.get(index);
    }
}

使用 JUC 包下提供的 CopyOnWriteArrayList 類

  • 其實 ConcurrentHashMap 也是 JUC 包下的

這里具體討論一下 CopyOnWriteArrayList 這個類,它采用了“寫時復制”的技術,也就是說,每當要往這個 list 中添加元素時,并不是直接就添加了,而是會先復制一份 list,然后在這個復制中添加元素,最后再修改指針的指向,看看 add 的源碼:

public boolean add(E e) {
    synchronized (lock) {
        //得到當前的數組
        Object[] es = getArray();
        int len = es.length;
        //復制一份并擴容
        es = Arrays.copyOf(es, len + 1);
        //把新元素添加進去
        es[len] = e;
        //修改指針的指向
        setArray(es);
        return true;
    }
}

有人可能會疑惑,這有什么意義,這不也加了 synchronized 嗎,而且還要復制數組,這**不是比 Vector 還要爛嗎?

確實是這樣的,在寫操作比較多的場景下,CopyOnWriteArrayList 確實比 Vector 還要慢,但它有兩個優勢:

雖然寫操作爛了,但讀操作快了很多,因為在 vector 中,讀操作也是需要鎖的,而在這里,讀操作就不需要鎖了,get 方法比較短可能不便于理解,我們看看 indexOf 這個方法:

public int indexOf(Object o) {
    Object[] es = getArray();
    return indexOfRange(o, es, 0, es.length);
}
private static int indexOfRange(Object o, Object[] es, int from, int to) {
    if (o == null) {
        for (int i = from; i < to; i++)
            if (es[i] == null)
                return i;
    } else {
        //****here****
        for (int i = from; i < to; i++)
            if (o.equals(es[i]))
                return i;
    }
    return -1;
}

可以發現,這個方法先把當前數組 array 交給了 es 這個變量,后續的所有操作都是基于 es 進行的(此時 array 和 es 都指向內存中的同一份數組 a1)

由于所有寫操作都是在 a1 的拷貝上進行的(我們把內存中的這份拷貝稱為 a2),因此不會影響到那些正在 a1 上進行的讀操作,并且就算寫操作執行完畢了,array 指向了 a2,也不會影響到 es 這個數組,因為 es 指向的還是 a1

試想,如果 vector 的讀操作不加鎖會出現什么情況?由于 vector 中所有的讀寫操作都是基于同一個數組的,因此雖然讀操作一開始拿到的數組是沒問題的,但在后續遍歷的過程中(比如上面代碼標注了 here 的地方),很可能出現其他線程對數組進行了修改,夸張點說,如果有個線程把數組給清空了,那么讀操作就肯定會報錯了,而對于 CopyOnWriteArrayList 來說,就算有清空的操作,那也是在 a2 上進行的,而讀操作還是在 a1 上進行,不會有任何影響

在 forEach 遍歷一個 vector 時,是不允許對 vector 進行修改的,會報出 ConcurrentModificationException 這個異常,理由很簡單,因為只有一份數組,要是遍歷到一半有其它線程把數組清空了不就出問題了嗎,因此 java 干脆就直接禁止這種遍歷時修改數組的行為了,但對于 CopyOnWriteArrayList 來說,它的遍歷是一直在 a1 上進行的,其它寫線程只能修改到 a2,這對 a1 是沒有任何影響的,我們看一段代碼來驗證一下:

public class Test {
    public static void main(String[] args) {
        CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        //遍歷時把數組清空
        for (Integer i : list) {
            System.out.println(i);
            list.clear();
        }
    }
}

結果是沒有報錯,并且完整輸出了 0~999 所有的數字,可見這里遍歷的就是最開始的那個數組 a1,期間哪怕有再多的寫操作也不會影響到 a1,因為所有的寫操作都是在 a2 a3 a4 上進行的
綜上所述,CopyOnWriteArrayList 的優點有兩個:

  • 讀操作不需要鎖,因此讀讀可以并發,讀寫也能并發,性能較好

  • forEach 遍歷時也不需要鎖(其實遍歷也算是一種讀操作吧),主要是遍歷時數組可以被修改,不會報錯(因為遍歷的是 a1,改的是 a2 a3,對 a1 不會有影響)

但它的缺點也很明顯,主要有兩點:

  • 首先,寫操作的內存消耗非常大,每次修改數組都會進行一次拷貝,如果數組比較大或者修改次數比較多,很快就會消耗掉大量內存,觸發 GC,因此在寫多的場景下一定要慎用這個類

  • 其次,所有讀操作和 forEach 遍歷都是基于舊數組 a1 的,就算遍歷途中新增了一個很重要的數據,這個數據也是在 a2 中,遍歷 a1 是無法得到這個數據的,總之就是,所有的讀操作一旦開始,就無法再感知到最新的那些數據

可以發現一個有趣的事情,就是成也舊數組,敗也舊數組,正因為所有讀取都是基于舊數組 a1 的,因此可以不加鎖就大膽進行,不怕有線程把數組改了,因為改動都是在 a2 a3 上的,跟 a1 沒有關系,但也正因為所有讀取都是基于舊數組 a1 的,因此一旦讀取操作開始,就算有線程在數組中加入了一個很重要的數據,這個讀取操作也是感知不到這個最新的數據的,因為這個最新的數據只會在 a2 中有

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java中ArrayList線程不安全的解決方法”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

江华| 双峰县| 潼南县| 东乡族自治县| 东宁县| 开原市| 清涧县| 得荣县| 泰宁县| 策勒县| 清河县| 茌平县| 陵水| 赤城县| 淄博市| 两当县| 永济市| 新沂市| 静安区| 武义县| 来安县| 彰武县| 泊头市| 清徐县| 噶尔县| 长寿区| 卢氏县| 通化市| 澳门| 抚远县| 方山县| 万全县| 九龙坡区| 凤台县| 贞丰县| 玉环县| 台南市| 开化县| 兴文县| 满城县| 通化市|