瞄一眼CopyOnWriteArrayList(jdk11)

本文详细解析了CopyOnWriteArrayList在JDK11中的实现细节,包括其线程安全机制的变化,从ReentrantLock到synchronized的转换,以及构造函数、读写操作的具体实现。强调了其写时复制策略和内存一致性保障。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CopyOnWriteArrayList是ArrayList线程安全的变体。使用写时复制策略进行修改操作。

与之前版本较明显的区别是,jdk11中用来保护所有设值方法(mutator)的ReentrantLock改为使用关键字synchronized。

文档中也明确表示相比较于ReentrantLock更倾向于使用内置锁(We have a mild preference for builtin monitors over ReentrantLock when either will do.)。

两个都是可重入独占锁,在不涉及到中断、超时等情况时,编码时使用synchronized明显比ReentrantLock方便简洁得多。

 

CopyOnWriteArrayList的成员变量:

    //锁对象
    final transient Object lock = new Object();

    //存储数据的数组
    private transient volatile Object[] array;

    final Object[] getArray() {
        return array;
    }

    final void setArray(Object[] a) {
        array = a;
    }

 

第一个Object对象充当写时复制的锁对象,第二个volatile的array用来存放数据

 

 

挑个构造函数看看:

 1     /**
 2      * 根据Collection迭代器返回的顺序创建包含指定集合元素的列表
 3      *
 4      * @param c 最初保存元素的集合
 5      * @throws NullPointerException 如果指定的集合为null
 6      */
 7     public CopyOnWriteArrayList(Collection<? extends E> c) {
 8         Object[] es;
 9         if (c.getClass() == CopyOnWriteArrayList.class)
10             es = ((CopyOnWriteArrayList<?>)c).getArray();    //同类对象直接获取array赋值
11         else {
12             es = c.toArray();
13             // defend against c.toArray (incorrectly) not returning Object[]
14             // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
15             if (es.getClass() != Object[].class)
16                 es = Arrays.copyOf(es, es.length, Object[].class);
17         }
18         setArray(es);    //array赋值
19     }

注释中说,L15的条件分支是为了解决Collection.toArray()没有正确地返回为Object[]类型,而是错误地返回为Object类型。这个bug在jdk9已经被修复了。不知道为什么在11.0.2还留着。

jdk中的//单行注释一般都挺有意思的…

 

 

读操作和ArrayList没多大差别,所以都是弱一致性的。挑几个写操作看看:

 1   public void add(int index, E element) {
 2     synchronized (lock) { //获取独占锁
 3       Object[] es = getArray();
 4       int len = es.length;
 5       if (index > len || index < 0) { //越界校验
 6         throw new IndexOutOfBoundsException(outOfBounds(index, len));
 7       }
 8       Object[] newElements;
 9       int numMoved = len - index;
10       if (numMoved == 0) {  //若index=array.length,则新元素添加在末尾
11         newElements = Arrays.copyOf(es, len + 1);
12       } else {
13         newElements = new Object[len + 1];    //创建array副本,调用System.arraycopy()移动index后元素添加新元素于index
14         System.arraycopy(es, 0, newElements, 0, index);
15         System.arraycopy(es, index, newElements, index + 1, numMoved);
16       }
17       newElements[index] = element;
18       setArray(newElements);    //将修改后的array副本回写给array
19     }
20   }

 

 1   public E set(int index, E element) {
 2     synchronized (lock) { //获取独占锁
 3       Object[] es = getArray();
 4       E oldValue = elementAt(es, index);  //找出index位置的元素
 5       if (oldValue != element) {    //如果元素与原先元素不同,则创建array副本。在副本修改后写回array
 6         es = es.clone();
 7         es[index] = element;
 8         setArray(es);
 9       }
10       return oldValue;
11     }
12   }

 

值得注意的是,之前版本的set方法在上段代码L9多了个分支:

} else {
      // Not quite a no-op; ensures volatile write semantics
      setArray(elements);
}

注释说了,setArray()是为了保持volatile写的语义,即内存一致性:当存在并发时,将对象放入CopyOnWriteArrayList之前的线程中的操作happen-before随后通过另一线程从CopyOnWriteArrayList中访问或移除该元素的操作。由于jdk11使用synchronized替代了ReentrantLock,也就不需要这一段了。

 

转载于:https://www.cnblogs.com/niceboat/p/9978910.html

CopyOnWriteArrayList是一个线程安全的List实现,它通过每次修改操作(添加、删除、修改)时都创建一个新的底层数组来实现线程安全性。 CopyOnWriteArrayList的特点如下: 1. 线程安全:多个线程可以同时读取CopyOnWriteArrayList的内容,而不需要额外的同步机制。这使得它非常适合在读操作远远多于写操作的场景中使用。 2. 写操作的代价较高:每次对CopyOnWriteArrayList进行写操作时,都会创建一个新的底层数组,因此写操作的代价较高。 3. 实时性较低:由于写操作会创建新的底层数组,读取操作可能会看到旧的数据,因此CopyOnWriteArrayList的实时性较低。 使用CopyOnWriteArrayList的示例代码如下: ```java import java.util.concurrent.CopyOnWriteArrayList; public class Main { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); list.add("Hello"); list.add("World"); for (String item : list) { System.out.println(item); } } } ``` 在上述代码中,我们创建了一个CopyOnWriteArrayList,并向其中添加了两个元素。然后使用增强for循环遍历CopyOnWriteArrayList中的元素,并打印输出。 需要注意的是,CopyOnWriteArrayList适用于读操作远远多于写操作的场景,如果写操作非常频繁,可能会导致性能问题。此外,CopyOnWriteArrayList不保证元素的顺序性,因为在写操作时会创建新的底层数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值