CopyOnWriteArrayList源码解析

CopyOnWriteArrayList简介

CopyOnWriteArrayList是ArrayList的并发容器。
写操作:
通过名字就可以看出来其原理。CopyOnWriteArrayList会将对象数组拷贝到新数组中,当写的时候会写在新数组上,然后将CopyOnWriteArrayList的对象数组替换成新数组。
读操作:
读操作不会加任何锁

了解了写操作和读操作的做法后,我们发现CopyOnWriteArrayList适合于写少读多的场景。

CopyOnWriteArrayList源码解析

主要属性

//独占锁,当发生写操作时加的锁
final transient ReentrantLock lock = new ReentrantLock();
//数组
private transient volatile Object[] array;

get(int index)

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

get()没什么说的,常规操作!

add(E e)

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
        //获取array
        Object[] elements = getArray();
        int len = elements.length;
        //将array 拷贝到newElements
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //将e加入到newElements的最后位置
        newElements[len] = e;
        //将array引用设置为newElements
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

当add(e)的时候,有以下步骤:
1、将array拷贝到一个newElements中,newElements的长度是len+1
2、将e加入到newElements的最后位置中
3、将array引用设置为newElements

从源码我们可以看出,我们可以看出一个问题:
当线程调用add()的时候,这个时候如果有其他线程来读数据,会读不到新加入的数据,会出现短暂的读写不一致!

set(int index,E e)

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 {
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

remove(int index)

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];
            //将从elements的下标0复制index个元素到newElements的0下标开始的index个元素
            System.arraycopy(elements, 0, newElements, 0, index);
            //将从elements的下标index+1复制numMoved个元素到newElements的index下标开始的numMoved个元素
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            setArray(newElements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

总结

写操作的其他方法我们也不说了,都是这个套路,加个lock,然后拷贝一个新数组,将写操作写到新数组中,然后将新数组替换原来的数组。

读操作不加锁,读取的是原本的数组,有的时候会存在短暂的读写不一致!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值