CopyOnWriteArrayList

CopyOnWriteArrayList

写入时复制(CopyOnWrite)思想

  写入时复制(CopyOnWrite,简称COW)思想是计算机程序设计领域中的一种优化策略。其核心思想是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

概括一下CopyOnWriteArrayList源码注释介绍了什么:

  1. CopyOnWriteArrayList是线程安全容器(相对于ArrayList),底层通过复制数组的方式来实现。
  2. CopyOnWriteArrayList在遍历的使用不会抛出ConcurrentModificationException异常,并且遍历的时候就不用额外加锁
  3. 元素可以为null
  4. CopyOnWriteArrayList底层就是数组,加锁就交由ReentrantLock来完成
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;
        
        // 将volatile Object[] array 的指向替换成新数组
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

====在添加的时候就上锁,并复制一个新数组,增加操作在新数组上完成,将array指向到新数组中,最后解锁。
对于remove()、clear()set()和add()是类似的

部分源码:

/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();

/** The array, accessed only via getArray/setArray. */
private transient volatile 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;
}

array 数组是被 volatile修饰的,修改后可其他线程可立即察觉到这个修改

volatile :(挥发物、易变的):变量修饰符,只能用来修饰变量。修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变 化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
transient:(暂短的、临时的):修饰符,只能用来修饰字段。在对象序列化的过程中,标记为transient的变量不会被序列化。

总结:

  • 在修改时,复制出一个新数组,修改的操作在新数组中完成,最后将新数组交由array变量指向
  • 写加锁,读不加锁

缺点:

  • 内存占用:如果CopyOnWriteArrayList经常要增删改里面的数据,经常要执行add()、set()、remove()的话,那是比较耗费内存的
  • 数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性

你可能感兴趣的:(java,collection)