CopyOnWriteArrayList源码分析


title: JUC.CopyOnWriteArrayList

CopyOnWriteArrayList提纲

CopyOnWriteArrayList简介

CopyOnWriteArrayList是一个适用于都多写少的线程安全集合类,但是也需要明白的是读写分离的集合都会存在弱一致性。说人话就是会有脏读的情况出现呀。
不过总体来说是一个不错的工具类,今天孤尽老师说到有人用这个类来当写多读少的用。
我其实想了一下为什么这哥们会这么玩。应该是有两种情况
1.真不明白原理
2.因为不会用的别方式实现线程安全的集合,然后该集合又正好实现了线程安全的写。所以就用了。。。。当万精油用咯

CopyOnWriteArrayList源码解读

先看下类图
avatar
很简单的常规操作,数据结构是数组。内部实现一个迭代器,然后搞一个COWSublist用来获取集合某一部分数据,在ArrayList里面sublist有的坑这里照样有。
我猜想sublist方法为什么不拷贝一份快照,而是直接用原来的集合然后做一个比较是否改变。
看了一下ArrayList的注释有下面这段话

  • This method eliminates the need for explicit range operations (of

  • the sort that commonly exist for arrays). Any operation that expects
  • a list can be used as a range operation by passing a subList view
  • instead of a whole list. For example, the following idiom
  • removes a range of elements from a list:
  •  list.subList(from, to).clear();
    

所以我猜想了一下原因
1.本身该方法设计就是用来读取一个视图,视图参见数据库的视图。什么意思了?就不打算让你改呗
2.复制其实数据量大一点挺消耗内存的,节省资源提高性能?
咱们还是接着看代码,主要看读写分离的实现思路

private transient volatile Object[] array;//通过volatile保证可见性,但是其实也影响一点性能。因为每次都需要写回主内存之后,再从主内存强制加载到工作内存。

final Object[] getArray() {//获取数组
    return array;
}
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);//将新数组刷入到array,这时候由于array有volatile保证可见性。所以读线程能里面获取到最新的数据
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);//一样修改个啥嘛
        }
        return oldValue;//返回旧值
    } finally {
        lock.unlock();//释放锁
    }
}

public E get(int index) {
    return get(getArray(), index);//获取数据
}

好了上面的读写分离的实现就写完了,至于为啥适合多读少写。其实也很明白了
尼玛如果多写少读自己不会加一个锁实现?非要用这个?每次都拷贝一份数据,良心不会痛?
然后咱们再说说sublist的坑,继续看下源代码

public List<E> subList(int fromIndex, int toIndex) {
    //常规操作哈,加一个锁保证数据不是变化的
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
            throw new IndexOutOfBoundsException();
        return new COWSubList<E>(this, fromIndex, toIndex);//主要看这个里面
    } finally {
        lock.unlock();
    }
}

// only call this holding l's lock将this给传进来了然后设置一下expectedArray
COWSubList(CopyOnWriteArrayList<E> list,
           int fromIndex, int toIndex) {
    l = list;
    expectedArray = l.getArray();
    offset = fromIndex;
    size = toIndex - fromIndex;
}
//以后视图的任何操作都会调用这个方法判断一下,所以拿了视图之后不要随意修改原先list的数据不然会爆炸。
private void checkForComodification() {
    if (l.getArray() != expectedArray)
        throw new ConcurrentModificationException();
}

好了这个类的源码就写完了,其实JDK集合的代码真的简单。没有什么特别变态的操作,除了1.8的ConcurrentHashMap的部分源码看的有点难受~

欢迎扫码加入知识星球继续讨论
avatar

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值