CopyOnWriteArrayList是concurrent包中的一个线程安全的ArrayList类,这个类通过在add,set等操作的时候拷贝一个新的数组,实现了线程安全。add,set等操作我们认为是write操作。这种在write的时候拷贝对象来实现线程安全的操作就叫做CopOnRight.
当需要对容器进行write操作时,先拷贝一个数组。再对数据进行write,然后再用新数组代替原数组。
接下来我们对比ArrayList和CopyOnWriteArrayList来分析:
// ArrayList
transient Object[] elementData;
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
// CopyOnWriteArrayList
private transient volatile Object[] array;
public E get(int index) {
return elementAt(getArray(), index);
}
static <E> E elementAt(Object[] a, int index) {
return (E) a[index];
}
可以看到CopyOnWriteArrayList和ArrayList除了多一个volatile字段来确保可见性,确保数组被写后可以马上被其他线程读取,并没有太多不同。
接下来我们以add为例,看看CopyOnWriteArrayList是怎么确保安全的。
// ArrayList
transient Object[] elementData;
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value),
* which helps when add(E) is called in a C1-compiled loop.
*/
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
// CopyOnWriteArrayList
/**
* The lock protecting all mutators. (We have a mild preference
* for builtin monitors over ReentrantLock when either will do.)
*/
final transient Object lock = new Object();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
可以看到,在add时候,ArrayList只有在数组长度不够时会创建新新的数组,而CopyOnWriteArrayList每次add的时候都会先上锁,然后创建一个新的数组并将数据写进去,最后用新的数组代替原有数组,释放锁资源。
其他set,remove等操作流程类似。
网友评论