【面試系列】併發容器之CopyOnWriteArrayList

微信公衆號:放開我我還能學

分享知識,共同進步!java

image

ArrayList 有什麼缺點?
  1. 非線程安全
  2. 迭代時沒法修改
你用過線程安全的集合嗎?

有,說在哪使用。數組

沒有,不過我瞭解過。安全

那你說說它們的實現。

Vector微信

Vector 自己比較低效,由於它的實現基本就是將 add、get、set 等各類方法加上 synchronized 鎖。這就致使了全部併發操做都要競爭同一把鎖,一個線程在進行同步操做時,其餘線程只能等待,大大下降了併發操做的效率。併發

Collections#SynchronizedList高併發

同步包裝器 SynchronizedList 雖然沒使用方法級別的 synchronized 鎖,可是使用了同步代碼塊的形式,本質上仍是沒有改進。spa

CopyOnWriteArrayList線程

CopyOnWriteArrayList 是一個寫時複製的容器,當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行 Copy,複製出一個新的容器,而後往新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。這樣作的好處是咱們能夠對 CopyOnWriteArrayList 進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此 CopyOnWriteArrayList 是一種讀寫分離的容器,適用於讀多寫少的場景,支持高併發讀取。code

CopyOnWriteArrayList 是如何保證寫時線程安全的?

使用了 ReentrantLock 獨佔鎖,保證同時只有一個線程對集合進行修改操做。對象

如何理解 CopyOnWrite 思想?

寫時複製。就是在寫的時候,拷貝一份原對象,只操做拷貝的對象,操做完後再覆蓋原對象,保證 volatile 語義。

CopyOnWriteArrayList 的缺點是什麼?
  1. 佔用內存問題。由於 CopyOnWrite 的寫時複製機制,因此在進行寫操做的時候,內存裏會同時駐紮兩個對象的內存,舊的對象和新寫入的對象,這樣若是數據很大可能形成頻繁的 GC。
  2. 數據一致性問題。由於 CopyOnWrite 容器支持讀寫分離,因此只能保證數據的最終一致性,不能保證明時一致性。
CopyOnWriteArrayList 在使用迭代器時是否有什麼注意事項?

咱們知道,CopyOnWriteArrayList 是底層使用一種安全失敗機制。也就是說,能夠在迭代時進行增刪改操做。

不過,在迭代器使用時有一個注意事項:迭代器獲取的數據取決於迭代器建立的時候,而不是迭代器迭代的時候。

請看下面示例:

public static void main(String[] args) {
    CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(new Integer[]{1, 2, 3});

    ListIterator<Integer> iterator1 = list.listIterator();
    list.add(4);
    ListIterator<Integer> iterator2 = list.listIterator();

    iterator1.forEachRemaining(System.out::print);  // 123
    System.out.println();
    iterator2.forEachRemaining(System.out::print);  // 1234
}

從上能夠看出,iterator1 在添加數據 4 以前就之前建立,那麼最終它遍歷獲取的數據是 123,而 iterator2 在添加完數據 4 以後才建立,那麼最終它遍歷獲取的數據是 1234。

下面我說下緣由。

image-20200504213216412

image-20200504213358237

無論是調用 iterator 方法獲取迭代器,仍是調用 listIterator 方法獲取迭代器,內部都會返回一個 COWIterator 對象。

image-20200504213546862

進入 COWIterator 構造方法查看,發如今構造方法中會把 array 數組賦值給 snapshot 變量,若是其餘線程沒有對 CopyOnWriteArrayList 進行增刪改的操做,那麼 snapshot 就是自己的 array,可是若是其餘線程對 CopyOnWriteArrayList 進行了增刪改的操做,那麼舊的數組會被新的數組給替換掉,可是 snapshot 仍是原來舊的數組的引用。也就是說,當咱們使用迭代器遍歷獲取數據時,不能保證拿到的數據是最新的。

image

獲取更多最新文章,關注公衆號【放開我我還能學】

image

相關文章
相關標籤/搜索