CopyOnWriteArrayList简介
CopyOnWriteArrayList,即“写入时复制”容器,在每次修改(add、remove)时都会创建新的底层基础数组,然后在新数组上修改,最后将新数组作为容器的基础数组。因此,非修改操作不需要加锁,修改操作时才加锁,并发性能较同步List好。
CopyOnWriteArrayList实现
以下定义了基础数组及数组操作
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;
}
/**
* Creates an empty list.
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
/**
* Creates a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection of initially held elements
* @throws NullPointerException if the specified collection is null
*/
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);
}
非修改操作:
// Positional Access Operations
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return (E)(getArray()[index]);
}
修改操作:
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
Object oldValue = 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 (E)oldValue;
} finally {
lock.unlock();
}
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
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();
}
}
优点
1、较同步List有更好的并发性能
2、其迭代器不需要加锁,且不会抛出ConcurrentModificationException,这正是迭代器所期望的。原因在于其内部迭代器实现COWIterator<E>自己持有了一个底层基础数组snapshot,在构造时传入,snapshot指向外部类CopyOnWriteArrayList的Object[] array;但CopyOnWriteArrayList有数据修改时将采取复制并创建 newArray,并将array指向newArray,但这并不影响snapshot指向原array。因此,迭代器不需要考虑与CopyOnWriteArrayList之间加锁,且迭代器实现为不允许修改数据,因此迭代器本身也不需要加锁。是一个非常好的设计。
缺点
1、每次修改容器时都会复制底层数组,需要一定开销,可能引起Minor GC或Full GC适用场景
仅当迭代操作远大于修改操作时,才应该使用“写入时复制”容器。一个典型的场景时“事件通知系统”,在分发通知时需要迭代已注册的监听列表,而注册和注销事件监听器的操作要远少于分发通知的操作.
public class VisualComponent {
private final List<KeyListener> keyListeners
= new CopyOnWriteArrayList<KeyListener>();
private final List<MouseListener> mouseListeners
= new CopyOnWriteArrayList<MouseListener>();
public void addKeyListener(KeyListener listener) {
keyListeners.add(listener);
}
public void addMouseListener(MouseListener listener) {
mouseListeners.add(listener);
}
public void removeKeyListener(KeyListener listener) {
keyListeners.remove(listener);
}
public void removeMouseListener(MouseListener listener) {
mouseListeners.remove(listener);
}
}