CopyOnWriteArrayList源码分析
基于jdk1.7源碼
一、無鎖容器
CopyOnWriteArrayList是JDK5中添加的新的容器,除此之外,還有CopyOnWriteArraySet、ConcurrentHahshMap和ConcurrentLinkedQueue等,它們都是無鎖容器。
所謂無鎖,就是不需要使用對象內置鎖(synchronized)或顯示加鎖(Lock),而是拋棄鎖機制而使用其它一些策略來保證線程安全。
這些無鎖容器背后的通用策略是:對容器的修改可以與讀取操作同時發生,只要讀取者只能看完成修改的結果即可。修改是在容器數據結構的某個部分的一個單獨的副本(有時是整個數據結構的副本)上執行的,并且這個副本在修改過程中是不可視的。只有當修改完成時,被修改的結構才會自動地與主數據結構進行交換,之后讀取者就可以看到這個修改了。
——from《java編程思想》p755
?
二、源碼分析
類聲明
public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable屬性
/** The lock protecting all mutators *//** 重入鎖*/transient final ReentrantLock lock = new ReentrantLock();/** The array, accessed only via getArray/setArray. *//** 數組,只能通過getArray/setArray訪問*/private volatile transient Object[] array;構造器
/*** 構造器(構造一個空的數組)*/public CopyOnWriteArrayList() {setArray(new Object[0]);}/*** 構造器(使用指定的容器來創建,按照原容器中元素的順序)。*/public CopyOnWriteArrayList(Collection<? extends E> c) {Object[] elements = c.toArray();// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elements.getClass() != Object[].class)elements = Arrays.copyOf(elements, elements.length, Object[].class);setArray(elements);}/*** 構造器(創建給定數組的拷貝)*/public CopyOnWriteArrayList(E[] toCopyIn) {setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));}CopyOnWriteArrayList在創建時,并沒有默認容量,這點跟ArrayList不同。
?
get方法
public E get(int index) {return get(getArray(), index);}final Object[] getArray() {return array;}讀操作沒有進行同步,因為CopyOnWriteArrayList的讀和寫分離。
add方法
public boolean add(E e) {final ReentrantLock lock = this.lock;//使用重入鎖加鎖 lock.lock();try {//現有數組Object[] elements = getArray();int len = elements.length;//創建新數組,并擴容1,用現有數組元素來填充(末尾為空)Object[] newElements = Arrays.copyOf(elements, len + 1);//將當前元素添加到數組尾部(在新數組上操作)newElements[len] = e;setArray(newElements);return true;} finally {//釋放鎖 lock.unlock();}}remove方法
public boolean remove(Object o) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (len != 0) {// Copy while searching for element to remove// This wins in the normal case of element being presentint newlen = len - 1;//新數組長度比原來少1//構建新數組Object[] newElements = new Object[newlen];//遍歷查找現有數組【不包含末位元素】for (int i = 0; i < newlen; ++i) {//若存在if (eq(o, elements[i])) {// found one; copy remaining and exit//將待刪除元素o的后續所有元素拷貝到新數組中【拷貝o后面所有元素】for (int k = i + 1; k < len; ++k)newElements[k-1] = elements[k];setArray(newElements);return true;} else//當前不是要找的目標元素o,順便將元素拷貝到新數組中【多次循環累計的這個操作相當于拷貝目標元素之前位置的所有元素】//【由此說明,即使不存在目標元素,依然要做拷貝現有數組的操作】newElements[i] = elements[i];}// special handling for last cell//【末位元素單獨處理】//如果末位元素是目標元素。此時,該拷貝的元素都已拷貝到新數組中,直接setArray(newElements)if (eq(o, elements[newlen])) {setArray(newElements);return true;}//如果末位元素仍然不是目標元素,說明不存在目標元素。//因為沒有setArray(newElements),所以原數組依舊是原數組。 }//現有元素數組為空,直接返回falsereturn false;} finally {lock.unlock();}}迭代器
public Iterator<E> iterator() {return new COWIterator<E>(getArray(), 0);}private static class COWIterator<E> implements ListIterator<E> {//快照數組private final Object[] snapshot;private int cursor;private COWIterator(Object[] elements, int initialCursor) {cursor = initialCursor;// snapshot = elements;}public boolean hasNext() {return cursor < snapshot.length;}public boolean hasPrevious() {return cursor > 0;}@SuppressWarnings("unchecked")public E next() {if (! hasNext())throw new NoSuchElementException();// return (E) snapshot[cursor++];}@SuppressWarnings("unchecked")public E previous() {if (! hasPrevious())throw new NoSuchElementException();// return (E) snapshot[--cursor];}public int nextIndex() {return cursor;}public int previousIndex() {return cursor-1;}/*** 迭代器不支持remove/set/add方法。*/public void remove() {throw new UnsupportedOperationException();}public void set(E e) {throw new UnsupportedOperationException();}public void add(E e) {throw new UnsupportedOperationException();}}?
?
總結
1.CopyOnWriteArrayList的工作原理?
CopyOnWriteArrayList在做修改操作時,每次都是重新創建一個新的數組,在新數組上操作,最終再將新數組替換掉原數組。因此,在做修改操作時,仍可以做讀取操作,讀取直接操作的原數組。讀和寫操作的目標都不同,因此讀操作和寫操作互不干擾,只有寫與寫之間需要進行同步等待。另外,原數組被聲明為volatile,這就保證了,一旦數組發生變化,則結果對其它線程(讀線程和其它寫線程)是可見的。
2.CopyOnWriteArrayList適合的應用場景?
正是由于每次修改都會創建新數組,內存中都要同時存有原數組和新數組,同時也要將元素拷貝到新數組中,內存開銷大,所以CopyOnWriteArrayList只適合于讀多寫少的場景。
比如某些系統級別的信息,往往只是需要加載或者修改很少的次數,但是會被系統內所有模塊頻繁的訪問,對于這種場景,我們最希望看到的就是讀操作盡可能地快,而寫即使慢一些也沒太大關系。
3.CopyOnWriteArrayList能否像ArrayList一樣支持fail-fast?
CopyOnWriteArrayList并不支持fail-fast機制,請參考CopyOnWriteArrayList不支持fail-fast機制。所以它的好處之一是當多個迭代器同時遍歷和修改這個列表時,不會拋出ConcurrentModificationException,因此也不必編寫特殊的代碼去防范這種異常。
4.CopyOnWriteArrayList的擴容?
CopyOnWriteArrayList并不像ArrayList一樣指定默認的初始容量。它也沒有自動擴容的機制,而是添加幾個元素,長度就相應的增長多少。
①因為CopyOnWriteArrayList適用于讀多寫少,既然是寫的情況少,則不需要頻繁擴容。
②修改操作每次在生成新的數組時就指定了新的容量,也就相當于擴容了,所以不需要額外的機制來實現擴容。
轉載于:https://www.cnblogs.com/rouqinglangzi/p/10115658.html
總結
以上是生活随笔為你收集整理的CopyOnWriteArrayList源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flutter Android/iOS包
- 下一篇: 「洛谷P1343」地震逃生 解题报告