JUC並行系列(四):【面試常問】多種方法解決ArrayList非執行緒安全,詳解CopyOnWriteArrayList(程式碼範例)

2020-09-25 11:00:27

跑起一個程式,並不難;難的是,能讓程式跑多遠!—— 一顆剽悍的種子

在這裡插入圖片描述
JUC並行系列

JUC並行系列(一):什麼?聽說你搞混了並行和並行
JUC並行系列(二):詳解Condition實現精準通知喚醒
JUC並行系列(三):面試問並行,一問鎖就懵(怒肝一篇透徹理解鎖,面試不慌)

一、ArrayList非執行緒安全

在之前的多執行緒系列中有講過ArrayList在多執行緒下並不安全。

深入多執行緒十:只有從不同案例中,才能深刻體會多執行緒的不安全,從而才能更好的解決

沒看過的小夥伴也沒關係,我們再把程式碼拿出來回顧一下。

1.1 ArrayList非執行緒安全程式碼範例

public class Demo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

執行結果

可以看到集合的長度新增的數量並不是我們預期的長度。

而是可能會出現覆蓋。

在多執行緒下集合ArrayList是不安全的。

在這裡插入圖片描述

二、Vector解決ArrayList非執行緒安全

是不是很好奇,這裡為何會提Vector,不只因為Vector是執行緒安全的,也因為ArrayList都來自List介面,所以可能是你學習時最常接觸的,也可能是你開發中解決 ArrayList 非執行緒問題最常用的,當然也會是你脫口而出的面試答案。

但是,但是…

如果你只會 Vector 是遠遠不夠的,為什麼這麼說,問你個小問題!

你覺得是 ArrayList 在JDK中先出現的還是 Vector

你的回答肯定是先 ArrayList 後再 Vector 啦,這還有問的,Vector不正是為了解決ArrayList的非執行緒問題的。

但是答案可能恰恰相反,在 JDK1.0 中就加入了 Vector,而ArrayList是在JDK1.2才加入的。所以 Vector是老生了,ArrayList才是那個新生(當然1.2也不算新啦)。

在這裡插入圖片描述
在這裡插入圖片描述

2.1 Vector解決ArrayList非執行緒程式碼範例

Vector解決起來很簡單,只需把ArrayList關鍵字換成 Vector。

 Vector<String> list = new Vector<String>();

Vector除了

public class Demo {
    public static void main(String[] args) {
        Vector<String> list = new Vector<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

執行結果
在這裡插入圖片描述

2.2 Vector雖好,但是慎用

Vector雖然是一個執行緒安全的List,但是它的執行緒安全實現方式是對所有操作都加上了synchronized關鍵字,這種方式嚴重影響效率.所以並不推薦使用Vecto。

在這裡插入圖片描述

2.3 面試回答Vector,並不是一個加分項,只是一個答案

除了慎用,當邂逅面試時,如果僅僅答出Vector其實並不能給你加分,因為Vector算是古老的關鍵字了,只能算是一個答案。

但是如果你能回答Vector後,還能說出下面解決的辦法,那麼就可以體現你知識面的廣度了。

三、 synchronizedList解決ArrayList非執行緒安全

synchronizedList是Collections提供的一個方法,解決起來也很簡單。

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

3.1 synchronizedList解決ArrayList非執行緒程式碼範例

public class Demo {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList());
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

執行結果
在這裡插入圖片描述

3.2 synchronizedList的優缺點

synchronizedList 的寫操作效能比下面即將要介紹的 CopyOnWriteArrayList 在多執行緒操作時效能好很多,但讀操作採用了synchronized關鍵字,所以讀沒有CopyOnWriteArrayList要好。

只要有synchronized的地方,也就是有加鎖的地方,效能方面當然就沒有不加鎖好。

所以沒有最好的方法,只有最合適的應用場景。

四、 CopyOnWriteArrayList

CopyOnWriteArrayList 主要在於 CopyOnWrite 寫入時複製(簡稱COW),是計算機程式設計中的一種優化策略。

寫入時複製其思想是在多執行緒下同時讀取同一個資源時會共同獲取指標指向的資源,但當某些執行緒操作(修改)同一個資源時,才會真正複製一份副本,而其他執行緒存取最初資源不變。

可以簡單的理解是 在寫入的時候避免覆蓋而造成資料問題。

4.1 CopyOnWriteArrayList解決ArrayList非執行緒程式碼範例

public class Demo {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

執行結果
在這裡插入圖片描述

4.2 總結CopyOnWriteArrayList

綜合上面,我想你都能總結 CopyOnWriteArrayList了,CopyOnWriteArrayList在讀方面好於Vector,synchronizedList,但讀寫效能不如synchronizedList。

五、最後

最後的最後,為了更好的閱讀體驗,我把想說的話都放在了下面,嘿嘿。

我是一顆剽悍的種子 把我會的,認真的分享 是我寫部落格一直不變的信條。
如果你能看到這篇博文,說明咱們還是很有緣的;希望能帶給你一些許幫助,創作的不易,
把我文章的知識帶走,你的三連留下,點贊,評論,關注,是我最大的動力。