一、概述
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 extends E> 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 extends E> c) {...}
public boolean addAll(int index, Collection extends E> 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 的核心思想是通过在修改时复制底层数组来实现线程安全性。这种实现方式有一些优点和缺点,下面我们来分别介绍一下。
优点:
- 高效的读取操作:由于读取操作不需要加锁,因此可以避免读写冲突导致的等待时间,从而提高读取操作的效率。
- 线程安全性:通过在修改时复制底层数组来实现线程安全性,从而避免了在修改操作中的读写冲突。
缺点:
- 内存占用:由于每次修改都需要复制底层数组,因此会占用更多的内存。
- 写操作效率低:由于每次写操作都需要复制底层数组,因此写操作的效率较低。
综上所述,CopyOnWriteArrayList 在读多写少的场景下具有较好的性能表现,但在写多读少的场景下则表现较差。因此,在使用 CopyOnWriteArrayList 时需要根据实际情况进行选择。
六、CopyOnWriteArrayList使用场景
最后,我们还需要注意一些 CopyOnWriteArrayList 的使用限制和注意事项:
- 只适用于小型列表:由于每次修改都需要复制底层数组,因此 CopyOnWriteArrayList 只适用于小型列表。对于大型列表,使用 CopyOnWriteArrayList 可能会导致内存占用过高。
- 适用于读多写少的场景:由于每次写操作都需要复制底层数组,因此 CopyOnWriteArrayList 适用于读多写少的场景。
- 不支持并发修改:由于每次修改都需要复制底层数组,因此 CopyOnWriteArrayList 不支持并发修改。如果需要支持并发修改,请使用其他线程安全的 List 实现。
- 迭代器快照:由于每次写操作都会复制底层数组,因此迭代器会返回一个快照,而不是列表的实时视图。如果在迭代过程中修改列表,可能会导致 ConcurrentModificationException 异常。
总之,CopyOnWriteArrayList 是一个非常有用的线程安全的 List 实现,它通过在修改时复制底层数组来实现线程安全性。在使用 CopyOnWriteArrayList 时需要注意其使用限制和注意事项,以充分发挥其优点,避免其缺点。