CopyOnWriteArrayList

本文深入解析了CopyOnWriteArrayList的get()和set()方法,对比ArrayList,详细阐述了其如何利用final和volatile保证并发读写的一致性和安全性。

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

CopyOnWriteArrayList


小渣渣,如有什么问题欢迎指正。

JDK版本1.8

get()方法

CopyOnWriteArrayList中的get方法

private transient volatile Object[] array;
public E get(int index) {
    return get(getArray(), index);
}

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

private E get(Object[] a, int index) {
    return (E) a[index];
}

get(index)方法调用内部的getArray()获得数组,再通过get(array,index)来获取元素

ArrayList中的get方法

transient Object[] elementData; 
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

E elementData(int index) {
    return (E) elementData[index];
}

rangeCheck()方法是用来检查index是否超出数组大小,不管它。get(index)方法调用了elementData(index)方法直接返回了数组元素。

区别
很明显两者区别在于CopyOnWriteArrayList中多了一步getArray(),可以看到getArray()方法的修饰符:final。
final字段修饰了getArray()方法,final修饰符在修饰变量时在其后增加了一层sfence,这一层sfence禁用了sfence前后指令的重排序,使得调用该方法之前对数组的修改都是可见的,也就保证了并发的数据一致性。

而CopyOnWriteArrayList中的array是一个volatile变量,读写操作都具有Happends-Before关系。

set()方法

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();
    }
}

可以看到set方法定义了一个可重入锁,是一个独占锁,并在一进入set方法就进行加锁,然后在一个副本newElements上修改元素,然后替换掉原来的数组。
这个锁保证了每次只有一个线程进行写操作,但是其它线程会读取数组数据,而出现读写竞争的情况。其中array是volatile类型变量,而先set方法现在副本上修改,然后修改数组的引用,使得这一瞬间的修改对于其它线程都是可见的,保证了数据一致性,也就是“写时复制”。
对于其它线程来说,拿到的副本可能是失效的,但是一定是集合某一瞬间的快照,一定程度上满足不变形,满足弱一致性。

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、付费专栏及课程。

余额充值