CopyOnWriteArrayList是一个并发容器。有很多人称它是线程安全的,我认为这句话不严谨, 缺少一个前提条件,那就是非复合场景下操作它是线程安全的。
CopyOnWriteArrayList (免锁容器)的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出 ConcurrentModificationException。 在 CopyOnWriteArrayList 中,写入将导致创建 整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安 全地执行。
CopyOnWriteArrayList 简介
public class CopyOnWriteArrayList
imp1ements List
}
在很多应用场景中,读操作可能会远远大于写操作。由于读操作根本不会修改原有的数据,因此如果每次读取都进行加锁操作,其实是一种资源浪费。我们应该允许多个线程同时访问List 的内部数据,毕竟读操作是线程安全的。
这和ReentrantReadWriteLock读写锁的思想非常类似,也就是读读共享、写写互斥、读写 互斥、写读互斥。JDK中提供了 CopyOnWriteArrayList类,相比于在读写锁的思想又更进一步。为了将读取的性能发挥到极致,CopyOnWriteArrayList读取是完全不用加锁的,并且更 厉害的是:写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待,读操作的性能 得到大幅度提升。
CopyOnWriteArrayList 是如何做到的
CopyOnWriteArrayList类的所有可变操作(add, set等等)都是通过创建底层数组的新副本 来实现的。当List需要被修改的时候,并不直接修改原有数组对象,而是对原有数据进行一 次拷贝,将修改的内容写入副本中。写完之后,再将修改完的副本替换成原来的数据,这样就 可以保证写操作不会影响读操作了。
从 CopyOnWriteArrayList 的名字可以看出,CopyOnWriteArrayList 是满足 CopyOnWrite 的 ArrayList,所谓CopyOnWrite的意思:、就是对一块内存进行修改时,不直接在原有内存块 中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后,再将原来指向的内存指针指到新的内存,原来的内存就可以被回收。
CopyOnWriteArrayList读取和写入源码简单分析
1、CopyOnWriteArrayList 读取操作的实现
读取操作没有任何同步控制和锁操作,理由就是内部数组array不会发生修改,只会被另外 —个array替换,因此可以保证数据安全。
/** The array, accessed only via getArray/setArray.*/
private transient volatile Object[] array;
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
final Object[] getArray() {
return array;
}
2、CopyOnWriteArrayList 写入操作的实现
CopyOnWriteArrayList写入操作add()方法在添加集合的时候加了锁,保证同步,避免多线 程写的时候会copy出多个副本。
*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) {
final ReentrantLock lock = this, lock;
lock.lock() ; // 加锁
try {
Object [] elements = get Array();
int len = elements.length;
Object[] newElements = Arrays.copyOf (elements, len + 1) ; // 拷贝新数组
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock, unlock() ; // 释放锁
}
}
Was this helpful?
0 / 0