CopyOnWriteArrayList實現原理及源碼分析

  CopyOnWriteArrayList是Java併發包中提供的一個併發容器,它是個線程安全且讀操做無鎖的ArrayList,寫操做則經過建立底層數組的新副原本實現,是一種讀寫分離的併發策略,咱們也能夠稱這種容器爲"寫時複製器",Java併發包中相似的容器還有CopyOnWriteSet。本文會對CopyOnWriteArrayList的實現原理及源碼進行分析。數組

實現原理

  咱們都知道,集合框架中的ArrayList是非線程安全的,Vector雖是線程安全的,但因爲簡單粗暴的鎖同步機制,性能較差。而CopyOnWriteArrayList則提供了另外一種不一樣的併發處理策略(固然是針對特定的併發場景)。安全

  不少時候,咱們的系統應對的都是讀多寫少的併發場景。CopyOnWriteArrayList容器容許併發讀,讀操做是無鎖的,性能較高。至於寫操做,好比向容器中添加一個元素,則首先將當前容器複製一份,而後在新副本上執行寫操做,結束以後再將原容器的引用指向新容器。併發

  優缺點分析框架

  瞭解了CopyOnWriteArrayList的實現原理,分析它的優缺點及使用場景就很容易了。源碼分析

  優勢:性能

  讀操做性能很高,由於無需任何同步措施,比較適用於讀多寫少的併發場景。Java的list在遍歷時,若中途有別的線程對list容器進行修改,則會拋出ConcurrentModificationException異常。而CopyOnWriteArrayList因爲其"讀寫分離"的思想,遍歷和修改操做分別做用在不一樣的list容器,因此在使用迭代器進行遍歷時候,也就不會拋出ConcurrentModificationException異常了this

  缺點:spa

  缺點也很明顯,一是內存佔用問題,畢竟每次執行寫操做都要將原容器拷貝一份,數據量大時,對內存壓力較大,可能會引發頻繁GC;二是沒法保證明時性,Vector對於讀寫操做均加鎖同步,能夠保證讀和寫的強一致性。而CopyOnWriteArrayList因爲其實現策略的緣由,寫和讀分別做用在新老不一樣容器上,在寫操做執行過程當中,讀不會阻塞但讀取到的倒是老容器的數據。線程

源碼分析

  基本原理了解了,CopyOnWriteArrayList的代碼實現看起來就很容易理解了。code

public boolean add(E e) { //ReentrantLock加鎖,保證線程安全
        final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; //拷貝原容器,長度爲原容器長度加一
            Object[] newElements = Arrays.copyOf(elements, len + 1); //在新副本上執行添加操做
            newElements[len] = e; //將原容器引用指向新副本
 setArray(newElements); return true; } finally { //解鎖
 lock.unlock(); } }  

  添加的邏輯很簡單,先將原容器copy一份,而後在新副本上執行寫操做,以後再切換引用。固然此過程是要加鎖的。

  刪除操做

 public E remove(int index) { //加鎖
        final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) //若是要刪除的是列表末端數據,拷貝前len-1個數據到新副本上,再切換引用
                setArray(Arrays.copyOf(elements, len - 1)); else { //不然,將除要刪除元素以外的其餘元素拷貝到新副本中,並切換引用
                Object[] newElements = new Object[len - 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); } return oldValue; } finally { //解鎖
 lock.unlock(); } }

  刪除操做同理,將除要刪除元素以外的其餘元素拷貝到新副本中,而後切換引用,將原容器引用指向新副本。同屬寫操做,須要加鎖。

  咱們再來看看讀操做,CopyOnWriteArrayList的讀操做是不用加鎖的,性能很高。

public E get(int index) { return get(getArray(), index); }

  直接讀取便可,無需加鎖

 private E get(Object[] a, int index) { return (E) a[index]; }

 總結

  本文對CopyOnWriteArrayList的實現原理和源碼進行了分析,並對CopyOnWriteArrayList的優缺點也進行了分析(Java併發包中還提供了CopyOnWriteSet,原理相似)。其實所謂併發容器的優缺點,無非是取決於咱們在面對特定併發場景時,是否能作出相對合理的選擇和應用。也但願本文能幫助到有須要的童鞋,共勉。

相關文章
相關標籤/搜索