赞
踩
CopyOnWriteArrayList
是java.util.concurrent包中提供的一个线程安全的ArrayList。它通过一种称为“写时复制”(Copy-On-Write)的方法来实现线程安全。简而言之,每当我们尝试修改这个列表(如添加、删除元素)时,它实际上并不直接在当前的列表上进行修改,而是先将当前列表复制一份,然后在这个副本上进行修改,修改完毕后再将原列表的引用指向新修改过的列表。这种机制确保了在读操作期间数据的不变性,非常适合读多写少的场景。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { // 版本序列号 private static final long serialVersionUID = 8673264195747942595L; // 可重入锁定 final transient ReentrantLock lock = new ReentrantLock(); // 对象数组,用于存放元素 CopyOnWriteArrayList 核心,使用volatile保证array的可见性和有序性。 private transient volatile Object[] array; // Unsafe 类 private static final sun.misc.Unsafe UNSAFE; // lock域的内存偏移量 private static final long lockOffset; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> k = CopyOnWriteArrayList.class; lockOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("lock")); } catch (Exception e) { throw new Error(e); } } }
// 向列表末尾添加一个元素 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; // 设置数组 setArray(newElements); return true; } finally { // 释放锁 lock.unlock(); } } // 此函数是移除列表上的元素-返回旧值 public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; // 获取旧值 E oldValue = get(elements, index); // 获取需要移动的元素个数 int numMoved = len - index - 1; // 需要移动的个数为0 if (numMoved == 0) // 直接复制一个 setArray(Arrays.copyOf(elements, len - 1)); else { // 新生数组 Object[] newElements = new Object[len - 1]; // 复制index之前的数据 System.arraycopy(elements, 0, newElements, 0, index); // 复制index之后的数据 System.arraycopy(elements, index + 1, newElements, index, numMoved); // 设置数组 setArray(newElements); } // 返回旧值 return oldValue; } finally { lock.unlock(); } } // 此函数用于用指定的元素替代此列表指定位置上的元素,也是基于数组的复制来实现的。 public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { // 获取数组 Object[] elements = getArray(); // 获取对应下标中元素的数据 E oldValue = get(elements, index); // 判断旧数据与新数据是否一致 if (oldValue != element) { // 获取数组长度 int len = elements.length; // 复制数组 Object[] newElements = Arrays.copyOf(elements, len); // 更新对应数组下标的数据 newElements[index] = element; // 设置数组 setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics // 设置数组 setArray(elements); } // 返回旧值 return oldValue; } finally { lock.unlock(); } }
// 默认构造器 public CopyOnWriteArrayList() { // 设置数组 setArray(new Object[0]); } public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) // 类型相同 // 获取c集合的数组 elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { // 类型不相同 // 将c集合转化为数组并赋值给elements elements = c.toArray(); // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elements.getClass() != Object[].class) // elements类型不为Object[]类型 // 将elements数组转化为Object[]类型的数组 elements = Arrays.copyOf(elements, elements.length, Object[].class); } // 设置数组 setArray(elements); }
CopyOnWriteArrayList
的内部类 COWIterator
是一个实现了 ListIterator
接口的迭代器类,专门为 CopyOnWriteArrayList
定制。它提供了一种安全的方式来遍历 CopyOnWriteArrayList
,即使在多线程环境中也不会抛出 ConcurrentModificationException
。我们来逐个分析这个内部类的主要部分。
static final class COWIterator<E> implements ListIterator<E> { // 数组快照,所有的迭代操作都是在这个数组快照上进行的,保证了在迭代过程中,即使原列表被修改,迭代器仍然可以保持一致的视图。后续在使用示例中会表示出来。 private final Object[] snapshot; // 这个字段是一个指针,指向下一个要返回的元素的索引 private int cursor; // 构造器 private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } // 判断是否还有下一个元素 public boolean hasNext() { return cursor < snapshot.length; } // 判断是否还有上一个元素 public boolean hasPrevious() { return cursor > 0; } // 用于获取下一个元素,并更新cursor的位置 @SuppressWarnings("unchecked") public E next() { if (! hasNext()) throw new NoSuchElementException(); return (E) snapshot[cursor++]; } // 用于获取下一个元素,并更新cursor的位置 @SuppressWarnings("unchecked") public E previous() { if (! hasPrevious()) throw new NoSuchElementException(); return (E) snapshot[--cursor]; } // 返回下一个元素索引 public int nextIndex() { return cursor; } // 返回上一个元素索引 public int previousIndex() { return cursor-1; } // 不支持操作 public void remove() { throw new UnsupportedOperationException(); } // 不支持操作 public void set(E e) { throw new UnsupportedOperationException(); } // 不支持操作 public void add(E e) { throw new UnsupportedOperationException(); } // 允许使用 lambda 表达式或方法引用来迭代剩余的元素 @Override public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); Object[] elements = snapshot; final int size = elements.length; for (int i = cursor; i < size; i++) { @SuppressWarnings("unchecked") E e = (E) elements[i]; action.accept(e); } cursor = size; } }
public class CopyOnWriteArrayListDemo { public static void main(String[] args) { CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>(); // 添加元素 cowList.add("Java"); cowList.add("Python"); cowList.add("C++"); // 创建线程来修改列表 Thread threadOne = new Thread(() -> { cowList.add("JavaScript"); cowList.remove("Python"); }); // 启动线程 threadOne.start(); // 同时进行迭代操作 Iterator<String> iterator = cowList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); try { Thread.sleep(100); // 延迟以模拟读取操作 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("=============更新之后============="); cowList.forEach(System.out::println); } }
在上述示例中,我们创建了一个CopyOnWriteArrayList
并在一个线程中对其进行了修改。同时,主线程中遍历了列表。由于CopyOnWriteArrayList
的特性,即使列表在遍历过程中被修改,迭代器也不会抛出ConcurrentModificationException
异常,因为迭代器是在列表被修改之前创建的。
CopyOnWriteArrayList
将非常适用。CopyOnWriteArrayList
提供了一种简单有效的解决方案。Vector
或Collections.synchronizedList
)可能会因为频繁的锁操作而导致性能问题。CopyOnWriteArrayList
通过减少锁的使用提供了一种高效的替代方案。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。