java.util.concurrent.CopyOnWriteArrayList
用于替代同步List有滑,在某些情況下它提供了更好的并發(fā)性能,并且在迭代期間不需要對容器進(jìn)行加鎖或復(fù)制。類似地,CopyOnWriteArraySet的作用是替代同步Set草丧。
"寫時復(fù)制(Copy-On-Write)"容器的線程安全性在于,只要正確地發(fā)布一個事實不可變的對象莹桅,那么在訪問該對象時就不再需要進(jìn)一步的同步昌执。在每次修改時,都會創(chuàng)建并重新發(fā)布一個新的容器副本诈泼,從而實現(xiàn)可變性懂拾。
CopyOnWriteArrayList 官方doc說明如下:
A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.
This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads. The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterator will not reflect additions, removals, or changes to the list since the iterator was created. Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.
All elements are permitted, including null.
Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a CopyOnWriteArrayList happen-before actions subsequent to the access or removal of that element from the CopyOnWriteArrayList in another thread.
接下來,結(jié)合 **JDK1.7 **源碼來分析CopyOnWriteArrayList內(nèi)部實現(xiàn)機(jī)制铐达。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/** The lock protecting all mutators */
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
final Object[] getArray() {
return array;
}
/**
* Sets the array.
*/
final void setArray(Object[] a) {
array = a;
}
/**
* 無參構(gòu)造函數(shù)
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
/**
* 帶Collection構(gòu)造函數(shù)
*/
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);
}
}
CopyOnWriteArrayList內(nèi)部的array數(shù)組被**volatile ** 修飾 能夠保證 array數(shù)組內(nèi)存可見性岖赋,當(dāng)某個線程改變了 array數(shù)組,其它線程能夠立刻看到瓮孙,所以CopyOnWriteArrayList 所有的讀操作都不用加鎖唐断,源碼如下:
/**以下皆為Read操作**/
/**
* 獲取指定位置元素
*/
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* 獲取集合元素個數(shù)
*/
public int size() {
return getArray().length;
}
/**
* 判斷集合是否為空
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* 判斷集合是否包含某個元素
*/
public boolean contains(Object o) {
Object[] elements = getArray();
return indexOf(o, elements, 0, elements.length) >= 0;
}
/**
* 查找某個元素位置
*/
public int indexOf(Object o) {
Object[] elements = getArray();
return indexOf(o, elements, 0, elements.length);
}
public int indexOf(E e, int index) {
Object[] elements = getArray();
return indexOf(e, elements, index, elements.length);
}
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
public int lastIndexOf(Object o) {
Object[] elements = getArray();
return lastIndexOf(o, elements, elements.length - 1);
}
public int lastIndexOf(E e, int index) {
Object[] elements = getArray();
return lastIndexOf(e, elements, index);
}
private static int lastIndexOf(Object o, Object[] elements, int index) {
if (o == null) {
for (int i = index; i >= 0; i--)
if (elements[i] == null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elements[i]))
return i;
}
return -1;
}
** volatile** 可以保證 可見性汁汗,但無法保證原子性,CopyOnWriteArrayList 所有的修改操作都需要通過 ReentrantLock 加鎖來保證任意時刻只有一個線程能夠修改CopyOnWriteArrayList 栗涂。
關(guān)于 ReentrantLock 內(nèi)部實現(xiàn)可以參考我的另外一篇文章:
JUC系列 - ReentrantLock源碼解析.
CopyOnWriteArrayList 幾個重要修改操作的源代碼如下:
/**以下皆為Write操作**/
/**
* 替換指定位置上的元素
*/
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
/**
* 向集合添加元素
*/
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();
}
}
/**
* 向集合指定位置添加元素
*/
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
/**
* 刪除指定元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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)
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();
}
}
/**
* 刪除元素
*/
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();
}
}
/**
* 刪除指定范圍段所有元素
* @throws IndexOutOfBoundsException if fromIndex or toIndex out of range
* ({@code{fromIndex < 0 || toIndex > size() || toIndex < fromIndex})
*/
private void removeRange(int fromIndex, int toIndex) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
int newlen = len - (toIndex - fromIndex);
int numMoved = len - toIndex;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, newlen));
else {
Object[] newElements = new Object[newlen];
System.arraycopy(elements, 0, newElements, 0, fromIndex);
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
} finally {
lock.unlock();
}
}
/**
*
*/
public boolean addIfAbsent(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// Copy while checking if already present.
// This wins in the most common case where it is not present
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = new Object[len + 1];
for (int i = 0; i < len; ++i) {
if (eq(e, elements[i]))
return false; // exit, throwing away copy
else
newElements[i] = elements[i];
}
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
可以看到,所有修改操作之前都必須先通過 lock.lock();
來加鎖祈争,修改完成后 通過 setArray(newElements);
來發(fā)布新的array數(shù)組斤程,最后通過 lock.unlock();
釋放鎖。
應(yīng)用場景
顯然菩混,每次修改容器時都會復(fù)制底層數(shù)組忿墅,這需要一定的開銷,特別是當(dāng)容器的規(guī)模較大時沮峡。僅當(dāng)?shù)僮鬟h(yuǎn)遠(yuǎn)多于修改操作時疚脐,才應(yīng)該使用"寫時復(fù)制(Copy-On-Write)"容器。例如事件通知系統(tǒng):在分發(fā)通知時需要迭代已注冊監(jiān)聽器鏈表邢疙,并調(diào)用每一個監(jiān)聽器棍弄,在大多數(shù)情況下,注冊和注銷事件監(jiān)聽器的操作遠(yuǎn)遠(yuǎn)少于接收事件通知的操作疟游。