如何正确使用CopyOnWriteArrayList实现线程安全的List操作

如何正确使用CopyOnWriteArrayList实现线程安全的List操作

编码文章call10242025-02-09 12:02:1911A+A-

一、概述

CopyOnWriteArrayList是Java中一个并发集合类,它是一个线程安全的List,可以被多个线程同时访问,而且它的实现方式与其他线程安全的集合类不同,它使用了Copy-On-Write(写时复制)的技术来保证线程安全。

二、CopyOnWriteArrayList原理

Copy-On-Write的原理是,当有一个线程要修改CopyOnWriteArrayList中的元素时,它并不是直接在原来的数组上修改,而是将原来的数组进行一次复制,在复制出的新数组上进行修改,这样其他线程仍然可以安全地读取原来的数组。修改完成后,它将新的数组替换掉原来的数组。这样做的好处是,修改操作不会影响其他线程的读操作,因为读操作一直都是读取原来的数组。

三、CopyOnWriteArrayList源码

CopyOnWriteArrayList的源码中,其内部使用了一个volatile修饰的数组来存储数据,这个数组的类型为Object[],这个数组就是CopyOnWriteArrayList中实际存储元素的地方。在进行写操作时,会先将原数组复制一份,然后在新数组上进行操作,最后再将新数组替换掉原数组。这样做虽然会有一定的开销,但是可以保证线程安全,同时也可以减少锁的使用。

CopyOnWriteArrayList提供了一些基本的操作,如add、remove、get、set等。需要注意的是,由于CopyOnWriteArrayList的实现方式,它的迭代器并不是强一致的,也就是说,在迭代的过程中,如果有其他线程修改了CopyOnWriteArrayList中的元素,迭代器可能会遗漏掉这些修改,因为它一直在迭代原来的数组。如果需要强一致性的迭代器,可以使用
Collections.synchronizedList方法将CopyOnWriteArrayList包装成一个线程安全的List,这样就可以获得强一致性的迭代器了。

总的来说,CopyOnWriteArrayList是一个线程安全的List,它使用了Copy-On-Write的技术来保证线程安全。虽然这样做会有一定的开销,但是可以减少锁的使用,从而提高并发性能。需要注意的是,由于CopyOnWriteArrayList的实现方式,它的迭代器并不是强一致的,如果需要强一致性的迭代器,可以使用
Collections.synchronizedList方法将CopyOnWriteArrayList包装成一个线程安全的List。

下面我们具体看一下CopyOnWriteArrayList的源码实现。

1.类的成员变量

CopyOnWriteArrayList类的主要成员变量包括:

java复制代码    /**
     * The lock protecting all mutators
     */
    final transient ReentrantLock lock = new ReentrantLock();
    /**
     * The array, accessed only via getArray/setArray.
     */
    private transient volatile Object[] array;

其中,lock是一个ReentrantLock对象,用于保证多线程对List进行修改时的同步;array是CopyOnWriteArrayList内部维护的一个数组,用于存储List中的元素。

2.构造方法

CopyOnWriteArrayList类提供了两个构造方法:

scss复制代码    /**
     * Creates a new, empty list.
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    /**
     * Creates a new list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection of initially held elements
     * @throws NullPointerException if the specified collection is null
     */
    public CopyOnWriteArrayList(Collection c) {
        Object[] elements;
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList)c).getArray();
        else {
            elements = c.toArray();
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elements.getClass() != Object[].class)
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }

第一个构造方法用于创建一个空的List,其内部维护的数组为空数组;第二个构造方法用于创建一个包含指定集合中元素的List,其中通过toArray方法获取集合中的元素,并将其复制到内部维护的数组中。

3.对外提供的方法

CopyOnWriteArrayList类提供了一系列方法,包括:

typescript复制代码    public int size() {...}
    public boolean isEmpty() {...}
    public boolean contains(Object o) {...}
    public Object[] toArray() {...}
    public  T[] toArray(T[] a) {...}
    public boolean add(E e) {...}
    public boolean remove(Object o) {...}
    public boolean containsAll(Collection c) {...}
    public boolean addAll(Collection c) {...}
    public boolean addAll(int index, Collection c) {...}
    public boolean removeAll(Collection....

4.核心方法

CopyOnWriteArrayList的核心方法包括:

  • add(E e):将指定元素添加到列表末尾。
  • add(int index, E element):在列表指定位置插入指定元素。
  • set(int index, E element):替换列表指定位置的元素。
  • remove(int index):移除列表指定位置的元素。
  • get(int index):返回列表指定位置的元素。
  • size():返回列表中元素的数量。
  • toArray():返回包含列表所有元素的数组。

五、CopyOnWriteArrayList优缺点

通过以上分析,我们可以发现 CopyOnWriteArrayList 的核心思想是通过在修改时复制底层数组来实现线程安全性。这种实现方式有一些优点和缺点,下面我们来分别介绍一下。

优点:

  1. 高效的读取操作:由于读取操作不需要加锁,因此可以避免读写冲突导致的等待时间,从而提高读取操作的效率。
  2. 线程安全性:通过在修改时复制底层数组来实现线程安全性,从而避免了在修改操作中的读写冲突。

缺点:

  1. 内存占用:由于每次修改都需要复制底层数组,因此会占用更多的内存。
  2. 写操作效率低:由于每次写操作都需要复制底层数组,因此写操作的效率较低。

综上所述,CopyOnWriteArrayList 在读多写少的场景下具有较好的性能表现,但在写多读少的场景下则表现较差。因此,在使用 CopyOnWriteArrayList 时需要根据实际情况进行选择。

六、CopyOnWriteArrayList使用场景

最后,我们还需要注意一些 CopyOnWriteArrayList 的使用限制和注意事项:

  1. 只适用于小型列表:由于每次修改都需要复制底层数组,因此 CopyOnWriteArrayList 只适用于小型列表。对于大型列表,使用 CopyOnWriteArrayList 可能会导致内存占用过高。
  2. 适用于读多写少的场景:由于每次写操作都需要复制底层数组,因此 CopyOnWriteArrayList 适用于读多写少的场景。
  3. 不支持并发修改:由于每次修改都需要复制底层数组,因此 CopyOnWriteArrayList 不支持并发修改。如果需要支持并发修改,请使用其他线程安全的 List 实现。
  4. 迭代器快照:由于每次写操作都会复制底层数组,因此迭代器会返回一个快照,而不是列表的实时视图。如果在迭代过程中修改列表,可能会导致 ConcurrentModificationException 异常。

总之,CopyOnWriteArrayList 是一个非常有用的线程安全的 List 实现,它通过在修改时复制底层数组来实现线程安全性。在使用 CopyOnWriteArrayList 时需要注意其使用限制和注意事项,以充分发挥其优点,避免其缺点。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4