CopyOnWriteArrayList是一個線程安全且在讀操作時候無鎖的ArrayList,其具體實現如下:
首先在CopyOnWriteArrayList內部定義了一個private類型的數組,並提供getter setter方法,不過需要注意的是該對象數組是被volatile關鍵字修飾的(關於volatile關鍵字可以參考我的博客“關於volatile的使用”一文),和ArrayList不同的是這裏創建一個大小爲0的數組,源碼如下:
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;
/**
* Creates an empty list.
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
然後我們來看看其add,remove等方法的實現
public boolean add(E e) {
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();
}
}
這裏使用了ReentrantLock首先調用了lock方法,然後獲取到對象數組,再調用Arrays.copyOf(elements,len+1)複製了一個長度加1的數組,緊接着把新增元素e添加到數組最後
一個位置上,再調用setArray方法將array引用的對象置換成新的對象,最後再釋放鎖,整個新增操作就完成了,這裏有一個疑惑就是作者爲什麼在複製數組的時候並沒有選擇
System.arraycopy,而使用的是Arrays.copyOf方法呢。下面是其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 present
int newlen = len - 1;
Object[] newElements = new Object[newlen];
for (int i = 0; i < newlen; ++i) {
if (eq(o, elements[i])) {
// found one; copy remaining and exit
for (int k = i + 1; k < len; ++k)
newElements[k-1] = elements[k];
setArray(newElements);
return true;
} else
newElements[i] = elements[i];
}
// special handling for last cell
if (eq(o, elements[newlen])) {
setArray(newElements);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
和add方法一樣,此處也是使用的ReentrantLock來保證線程安全的,首先創建比當前數組長度小1的數組,然後循環判斷如果相等就將數組後面元素前移,然後替換掉引用的對象。
public E get(int index) {
return (E)(getArray()[index]);
}
get方法就很直接了,沒有使用同步關鍵字和鎖,直接定位到數組指定位置。
最後我們來看看其迭代方法:
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
直接用存放數據的Object數組構造一個COWIterator對象,下面是主要代碼:
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
整個設計非常的巧妙,非常適合於讀多寫少的應用場景。而且相比直接使用同步關鍵字或鎖來說代價來得小,非常適用!