CopyOnWriteArrayList 讀寫分離,弱一致性


為什么會有CopyOnWriteArrayList?


 

我們知道ArrayList和LinkedList實現的List都是非線程安全的,於是就有了Vector,它是基於ArrayList的線程安全集合,但Vector無論是add方法還是get方法都加上了synchronized修飾,當多線程讀寫List必須排隊執行,很顯然這樣效率比較是低下的,那有沒有一種辦法讓效率提升,讓當讀List的時候線程是異步的,當寫List是同步的呢?答案是CopyOnWriteArrayList,他是讀寫分離的,好處是提高線程訪問效率,下面我們對比下CopyOnWriteArrayList和Vector執行效率。

import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-18 15:28
 * @description:安全list性能對比
 * @modified By:
 * 公眾號:叫練
 */
public class SafeListTest {

    private static Vector<String> safeList = new Vector<>();
    //private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();

    private static CountDownLatch countDownLatch = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {
        //初始化
        safeList.add("叫練");
        MySerive fishSerive = new MySerive();
        long start = System.currentTimeMillis();
        new Thread(()->{
            fishSerive.read();
            countDownLatch.countDown();
        },"叫練讀線程").start();
        new Thread(()->{
            fishSerive.write();
            countDownLatch.countDown();
        },"叫練寫線程").start();
        countDownLatch.await();
        System.out.println("花費:"+(System.currentTimeMillis()-start));
    }

    private static class MySerive {
        //
        public void read() {
            for (int i=0 ;i<1000000; i++) {
                safeList.get(0);
            }
        }

        //
        public void write() {
            for (int i=0 ;i<100000; i++) {
                safeList.add("叫練");
            }
        }
    }
}

 

如上代碼:當安全集合用Vector時,執行時長是100毫秒,當安全集合用CopyOnWriteArrayList時,執行時長是5000毫秒,神碼?你不是說CopyOnWriteArrayList的效率要高么?但執行情況CopyOnWriteArrayList執行的時長竟然是Vector的50倍!通過翻看源碼,我們發現當CopyOnWriteArrayList寫元素時是通過備份數組的方式實現的,當多線程同步激烈,數據量較大時會不停的復制數組,內存浪費嚴重。這就是時過長的原因!但是我們還是認可讀寫分離思想!

image.png

 

什么是弱一致性


import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-18 16:40
 * @description:CopyOnWriteArrayList弱一致性
 * @modified By:
 * 公眾號:叫練
 */
public class WeekCopyOnWriteArrayListTest {

    private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();
    //private static Vector<String> safeList = new Vector<>();

    public static void main(String[] args) throws InterruptedException {
        safeList.add("叫");
        safeList.add("練");
        Iterator<String> iterator = safeList.iterator();
        Thread thread = new Thread(()->{
            //刪除下標為0的元素
            safeList.remove(0);
        });
        thread.start();
        //主線程等待thread執行完成;
        thread.join();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

}

 

如上代碼:主線程等待thread子線程執行完畢,循環打印safeList元素,最終執行結果如下圖所示

 

 

你可能會有疑問,thread不是已經刪除“叫”嗎?控制台不是應該只打印一個“練”字嗎?為什么還會打出“叫練”兩個字,原因是main線程在執行Iterator<String> iterator = safeList.iterator();保存了元素快照,所以能看到這樣的執行結果,當thread線程執行完畢后,此時JVM內存狀態如下圖所示!

image.png

 

fail-safe特性


提到fail-safe,會先提到fail-fast,字面上翻譯快速失敗,它是集合快速檢測失敗機制,防止集合不正確操作!一般情況下,如果線程通過iterator方式循環集合時,另外一個線程也修改了這個集合,我們測試下,如上述測試弱一致性的代碼,將private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();換成private static Vector<String> safeList = new Vector<>();會發生什么情況呢?

image.png

如上圖,java.util.ConcurrentModificationException,集合並發修改錯誤,但換成CopyOnWriteArrayList執行正常,原因是CopyOnWriteArrayList刪除數據時會有集合快照。

所以他是fail-safe,而Vector是fail-fast!

總結


總結下吧,我們用代碼簡述說明了CopyOnWriteArrayList的讀寫分離,弱一致性,fail-safe,fail-safe等概念,並簡述了實現原理。喜歡的請點贊加關注哦。我是叫練【公眾號】,邊叫邊練。

image.png

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM