前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CopyOnWriteArrayList源码学习

CopyOnWriteArrayList源码学习

作者头像
写一点笔记
发布2020-08-25 14:26:06
3150
发布2020-08-25 14:26:06
举报
文章被收录于专栏:程序员备忘录

之前我们学习过乐观锁,大概得意思就是说当多线程来操作一个数据的时候,如果是读线程的时候,就获取读锁,写锁是不能降级为读锁的,但是写锁可以降级为读锁,而读锁也就是一个标志,用来防止写线程对数据的修改最终导致的脏数据问题,这样就使得多线程情况下,读的性能得到了极大的提升 。因为只有在写操作的时候,才会发生线程阻塞。而copyOnWriteArrayList的思想理念则是,在多线程修改的时候list值得时候,将list先拷贝一份。然后我修改完毕之后,将原来的数据的地址指向这个拷贝的list地址。这是针对写操作的线程,当在写操作的同时又有大面积读取的线程的时候,恰巧就是你list拷贝到一半的时候。那怎么办?是走老的list吗,如果走了老的list,那么读线程之间获取的值肯定是不一样的。那么只能在写操作的时候进行加锁了。但是如果写锁释放之后,那么之前读了脏数据的线程又来修改list的值,那么又变成了脏数据。在目前的认知里,我还是觉得读锁很重重要啊。那么就让我们看看CopyOnWriteArrayList是如何做这一切的。

代码还是挺多的。里边有几个迭代器。应该是进行内部操作的时候的一些工具。所以我们还是先不碰这些,后期应该会随着分析一步步的复现。

代码语言:javascript
复制
// Support for resetting lock while deserializing
private void resetLock() {
    UNSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock());
}
private static final sun.misc.Unsafe UNSAFE;
private static final long lockOffset;
static {
    try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> k = CopyOnWriteArrayList.class;
        lockOffset = UNSAFE.objectFieldOffset
            (k.getDeclaredField("lock"));
    } catch (Exception e) {
        throw new Error(e);
    }
}

看到了Unsafe操作内存的代码。

发现了volatile变量的数组,我们估计我们的数组元素应该都存储在这个array中了。而这个lock就是复制list时候的锁吧。

那么我们看看这块是如何初始化数组的。

代码语言:javascript
复制

    /**
     * Sets the array.
     */
    final void setArray(Object[] a) {
        array = a;
    }

    /**
     * Creates an empty list.
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

代码也很简单,基本都是直接赋值了。

代码语言:javascript
复制
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

添加元素的时候,是直接复制一份list,然后将新的list设置到volitile数组里。因为这块是加锁了,所以不会产生多个list副本,所以对于其他写线程来说并没有什么影响。因为volitile变量,所以其他线程之前读取了的值也会使用新的值。

总结:

CopyeOnWriteArrayList主要用于读多写少的场景。而且并发量比较大的情况。而且要求我们的list内容不能太大。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员备忘录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档