CopyOnWriteArrayList 是一个线程安全的 ArrayList 变体,它通过以下机制确保线程安全:
-
写时复制(Copy-on-Write)机制:当列表被修改时(例如添加、删除、设置元素值等操作),CopyOnWriteArrayList 首先会将当前底层数组复制一份,然后在这个副本上进行修改。完成修改后,它再将原来的数组引用指向新修改过的副本。这样,读操作总是在不变的数组版本上进行,从而避免了并发读写冲突。
-
内部锁定:所有的写入操作(增加、移除、更新等操作)都是通过一个内部的重入锁(ReentrantLock)来同步的,以此来保证同时只有一个线程可以对列表内容进行修改。 -
迭代器的弱一致性:CopyOnWriteArrayList 的迭代器支持弱一致性,意味着迭代器遍历列表时基于数组的一个快照,因此即使后续有修改,也不会抛出ConcurrentModificationException异常。迭代器看到的内容是创建迭代器时列表的状态,并不反映之后的修改。
详细写入实现逻辑请参考如下源代码:
public boolean add(E e) {synchronized (lock) {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;setArray(newElements);return true;}}
-
高成本的写操作:每次修改都需要复制整个底层数组,如果数组大小很大或者写操作很频繁,这将导致高额的内存占用和CPU资源消耗,并且每次写操作都需要加锁。
-
内存占用:为了创建数组的副本,系统需要额外的内存空间;在某些情况下,这可能导致内存溢出错误(如OutOfMemoryError)。 -
垃圾回收压力:由于频繁复制数组会产生大量不再使用的数组对象,这增加了垃圾收集器的工作负担,可能导致更频繁的垃圾收集事件。
- 事件监听器列表:通常注册监听器的操作不如触发事件频繁。
-
共享配置数据:配置数据在初始化时被加载,之后主要进行读取而很少修改。 -
发布/订阅模式中的订阅者列表:当消息相对少量更新时,读取操作(分发消息给订阅者)将比更新订阅者列表更频繁。
本篇文章来源于微信公众号: 互联网面试小帮手
微信扫描下方的二维码阅读本文

Comments NOTHING