总结
总结放前面防止太长不看
- CopyOnWriteArrayList是一个线程安全、并且在读操作时无锁的List实现。
- CopyOnWriteArrayList内部通过volatile数组来存储数据,通过
getArray
和setArray
维持语义,读操作不加锁,写操作复制原数组然后在复制的数组上面进行操作,完成操作之后再用新数组替换原数组。 - CopyOnWriteArrayList适合读多写少的并发场景,它的写操作效率极低,所有写操作共用一把锁,甚至在容量达到数组上限的时候添加元素只会用一个原数组的容量+1的新数组来代替它。
- 只能保证数据的最终一致性,不能保证数据的实时一致性。写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据。
重要方法
add
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 仅仅return 内部 array
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();
}
}
get
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
可以看出无论在什么时候获取数组都是使用的getArray()
,这个方法返回内部数组array,而对array的修改并不会在原数组上直接操作,而是先复制再操作,操作完之后替换原来的数组,这样不论在什么时候写和读都不会在同一个数组上操作而造成冲突。
set
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 {
// 为了保持“volatile”的语义,任何一个读操作都应该是一个写操作的结果,
// 也就是读操作看到的数据一定是某个写操作的结果(尽管写操作没有改变数据本身)。
// 所以这里即使不设置也没有问题,仅仅是为了一个语义上的补充(就如源码中的注释所言)
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
remove
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();
}
}
原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/copyonwritearraylist%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90/