首页 > 其他分享 >CopyOnWriteArrayList

CopyOnWriteArrayList

时间:2024-07-29 10:29:48浏览次数:12  
标签:容器 CopyOnWrite Object CopyOnWriteArrayList 线程 数组

ArrayList 是一个线程不安全的容器,如果在多线程环境下使用,需要手动加锁,或者使用 Collections.synchronizedList() 方法将其转换为线程安全的容器。否则,将会出现 ConcurrentModificationException 异常。

CopyOnWriteArrayList 是线程安全的,可以在多线程环境下使用。CopyOnWriteArrayList 遵循写时复制的原则,每当对列表进行修改(例如添加、删除或更改元素)时,都会创建列表的一个新副本,这个新副本会替换旧的列表,而对旧列表的所有读取操作仍然可以继续。

由于在修改时创建了新的副本,所以读取操作不需要锁定。这使得在多读取者和少写入者的情况下读取操作非常高效。当然,由于每次写操作都会创建一个新的数组副本,所以会增加存储和时间的开销。如果写操作非常频繁,性能会受到影响。

CopyOnWriteArrayList 适用于读操作远远大于写操作的场景,比如说缓存。因为 CopyOnWriteArrayList 采用写时复制的思想,所以写操作的性能较低,因此不适合写操作频繁的场景。

什么是 CopyOnWrite


读写锁 ReentrantReadWriteLock 是通过读写分离的思想来实现的,即读写锁将读写操作分别加锁,从而实现读写操作的并发执行。

但是,读写锁也存在一些问题,比如说在写锁执行后,读线程会被阻塞,直到写锁被释放后读线程才有机会获取到锁从而读到最新的数据,站在读线程的角度来看,读线程在任何时候都能获取到最新的数据,满足数据实时性

而 CopyOnWriteArrayList 是通过 Copy-On-Write(COW),即写时复制的思想来通过延时更新的策略实现数据的最终一致性,并且能够保证读线程间不阻塞。当然,这要牺牲数据的实时性

通俗的讲,CopyOnWrite 就是当我们往一个容器添加元素的时候,不直接往容器中添加,而是先复制出一个新的容器,然后在新的容器里添加元素,添加完之后,再将原容器的引用指向新的容器。多个线程在读的时候,不需要加锁,因为当前容器不会添加任何元素。

CopyOnWriteArrayList 原理


CopyOnWriteArrayList 内部维护的就是一个数组,被 volatile 修饰,能够保证数据的内存可见性。:

/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

get 方法

public E get(int index) {
    return get(getArray(), index);
}
/**
 * Gets the array.  Non-private so as to also be accessible
 * from CopyOnWriteArraySet class.
 */
final Object[] getArray() {
    return array;
}
private E get(Object[] a, int index) {
    return (E) a[index];
}

get 方法的实现非常简单,没有添加任何的线程安全控制,没有加锁也没有 CAS 操作,原因就是所有的读线程只会读取容器中的数据,并不会进行修改。

add 方法

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    // 使用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();
    }
}

// 根据 volatile 的 happens-before 规则,所以这个更改对所有线程是立即可见的
final void setArray(Object[] a) {
    array = a;
}

CopyOnWriteArrayList 的缺点


CopyOnWrite 容器有很多优点,但是同时也存在两个问题,即内存占用问题和数据一致性问题。

  • 内存占用问题:因为 CopyOnWrite 的写时复制机制,在进行写操作的时候,内存里会同时有两个对象,旧的对象和新写入的对象。

  • 数据一致性问题:CopyOnWrite 容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用 CopyOnWrite 容器,最好通过 ReentrantReadWriteLock 自定义一个的列表。

对比 CopyOnWrite 和读写锁。

相同点:

  • 两者都是通过读写分离的思想来实现的;

  • 读线程间是互不阻塞的

不同点:

  • 为了实现数据实时性,在写锁被获取后,读线程会阻塞;或者当读锁被获取后,写线程会阻塞,从而解决“脏读”的问题。而 CopyOnWrite 对数据的更新是写时复制的,因此读线程是延时感知的,但不会存在阻塞的情况。

标签:容器,CopyOnWrite,Object,CopyOnWriteArrayList,线程,数组
From: https://www.cnblogs.com/sprinining/p/18329577

相关文章

  • Doug Lea大师的佳作CopyOnWriteArrayList,用不好能坑死你!
    一、写在开头我们在学习集合或者说容器的时候了解到,很多集合并非线程安全的,在并发场景下,为了保障数据的安全性,诞生了并发容器,广为人知的有ConcurrentHashMap、ConcurrentLinkedQueue、BlockingQueue等,那你们知道ArrayList也有自己对应的并发容器嘛?作为使用频率最高的集合类之一,A......
  • 多线程(34)CopyOnWriteArrayList
    CopyOnWriteArrayList是Java中一个线程安全的ArrayList变体,属于java.util.concurrent包。它通过在所有修改操作(如add,set等)上执行显式复制来实现线程安全。这种设计适用于列表读操作的数量远远大于写操作的场景。设计原理CopyOnWriteArrayList的基本思想是,每当......
  • CopyOnWriteArrayList
    CopyOnWriteArrayList目录CopyOnWriteArrayListCopyOnWriteArrayList诞生记CopyOnWriteArrayList使用场景CopyOnWriteArrayList读写操作实现原理缺点源码分析CopyOnWriteArrayList诞生记代替Vector和SynchronizedList,就像ConcurrentHashMap代替SynchronizedMap的原因一样Vect......
  • 并发容器【ConcurentHashMap、CopyOnWriteArrayList、阻塞队列、ArrayBlockingQueue】
    (并发容器)转自极客时间什么是并发容器?在JUC包中,有一大部分是关于并发容器的,如ConcurrentHashMap,ConcurrentSkipListMap,CopyOnWriteArrayList及阻塞队列。这里将介绍使用频率、面试中出现频繁的最高的ConcurrentHashMap和阻塞队列。注意:这里说到的容器概念,相当于我们理解中......
  • 【并发编程】CopyOnWriteArrayList详解与原理
    ......
  • 线程安全集合(JDK1.5之前和之后)、CopyOnWriteArrayList、CopyOnWriteArraySet
    JDK1.5之前JDK1.5之前:Collections.synchronizedList JDK1.5之后CopyOnWriteArrayList   CopyOnWriteArraySet    ......
  • JUC高并发容器-CopyOnWriteArrayList
    CopyOnWriteArrayListJUC高并发容器线程安全的同步容器类什么是高并发容器?CopyOnWriteArrayListJUC高并发容器线程安全的同步容器类  Java同步容器类通过Synchronized(内置锁)来实现同步的容器,比如Vector、HashTable以及SynchronizedList等容器。线程安全的同步容器类主要有Vec......
  • jdk并发包 CopyOnWriteArrayList源码分析
    CopyOnWriteArrayList是jdk1.5并法包里面用于处理高并发下,读多写少的情况下,降低锁等待的集合类。下面对该类实现做一个简要的分析1,首先CopyOnWriteArrayList是实现了List接口,对=List接口的相关方法进行了实现。2,下面的它的add方法,会首先加锁,然后copy原List内部的数组,然后对新数组长......
  • 【Java 并发】【十】【JUC数据结构】【一】CopyOnWriteArrayList原理
    1 前言我们前面看过了volatile、synchronized以及AQS的底层原理,以及基于AQS之上构建的各种并发工具,ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier,那么我们这节该看什么了,是不是要看运用了。在日常的业务编程中经常使用到的内存数据结构有:Map、Set、List、Queue系列......
  • JUC-CopyOnWriteArrayList
    packagesyn;importjava.util.concurrent.CopyOnWriteArrayList;publicclassTestJUC{publicstaticvoidmain(String[]args){CopyOnWriteArrayListlist=new......