本站消息

站长简介/公众号


站长简介:逗比程序员,理工宅男,前每日优鲜python全栈开发工程师,利用周末时间开发出本站,欢迎关注我的微信公众号:幽默盒子,一个专注于搞笑,分享快乐的公众号

  价值13000svip视频教程,java大神匠心打造,零基础java开发工程师视频教程全套,基础+进阶+项目实战,包含课件和源码

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2021-03(283)

2021-04(154)

2021-05(100)

2021-06(41)

2021-07(7)

Java并发多线程编程——并发容器CopyOnWriteArrayList

发布于2021-06-12 16:20     阅读(695)     评论(0)     点赞(10)     收藏(4)


一、CopyOnWriteArrayList的理解

  • CopyOnWriteArrayList属于java.util.concurrent包;
  • CopyOnWriteArrayList使用到了Copy-On-Write思想,即写入时复制的容器
  • 当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
  • CopyOnWriteArrayList是ArrayList的一种线程安全的变体,在add、set、remove等操作会改变其内部值和长度的时候会通过创建一个新的数组来进行实现。
  • CopyOnWriteArrayList 读的时候不加锁,只有在写的时候才加锁,适用于读操作远远大于写操作。
  • 下图为jdk1.8API的解释:
    在这里插入图片描述

二、CopyOnWriteArrayList的类图

在这里插入图片描述

  • 由类图可知CopyOnWriteArrayList实现了List接口,List继承Collection接口,Collection继承Iterable接口,因此CopyOnWriteArrayList于List使用方法一致;

三、CopyOnWriteArrayList类中常用的方法

1、构造方法

  • CopyOnWriteArrayList() 创建一个空列表。
  • CopyOnWriteArrayList(Collection<? extends E> c) 按照集合的迭代器返回的顺序创建一个包含指定集合元素的列表。
  • CopyOnWriteArrayList(E[] toCopyIn) 创建一个包含给定数组的副本的列表。

2、常用方法

  • public boolean add(E e) 将指定的元素追加到此列表的末尾。
  • public boolean addAll(Collection<? extends E> c) 按照指定集合的迭代器返回的顺序将指定集合中的所有元素追加到此列表的末尾。
  • public E get(int index) 返回此列表中指定位置的元素。
  • public E set(int index, E element) 用指定的元素替换此列表中指定位置的元素。
  • public E remove(int index) 删除该列表中指定位置的元素。
  • public boolean removeAll(Collection<?> c) 从此列表中删除指定集合中包含的所有元素。
  • public Iterator iterator() 以正确的顺序返回该列表中的元素的迭代器。

四、CopyOnWriteArrayList常用方法的原理

1、 add(E e)方法

 /**
     * 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) {
    	//先加锁,因为ReentrantLock是独占锁,只有一个线程可以访问
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            //拷贝一个新的数组,此时数据的长度是被拷贝数组长度+1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //赋值
            newElements[len] = e;
            //将新的数组赋值给array
            setArray(newElements);
            return true;
        } finally {
            //释放锁
            lock.unlock();
        }
    }
    
     /**
     * Sets the array.
     * 将新的数组赋值给array
     */
    final void setArray(Object[] a) {
        array = a;
    }

2、get(int index)方法

 /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        return get(getArray(), index);
    }
     /**
     * Gets the array.  Non-private so as to also be accessible
     * from CopyOnWriteArraySet class.
     */
    final Object[] getArray() {
        return array;
    }

3、set(int index, E element)方法

/**
     * Replaces the element at the specified position in this list with the
     * specified element.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        //先加锁,因为ReentrantLock是独占锁,只有一个线程可以访问
        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();
        }
    }

4、remove(int index) 方法

/**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).  Returns the element that was removed from the list.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
       //先加锁,因为ReentrantLock是独占锁,只有一个线程可以访问
        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;
            //判断需要移除元素的下标在数组的哪个位置,然后进行相应的移除
            if (numMoved == 0)//如果是最后个位置
                //拷贝一个新的数组,此时数据的长度是被拷贝数组长度-1,
                //并将新的数组赋值给array
                setArray(Arrays.copyOf(elements, len - 1));
            else {//如果不是最后个位置,先进行拷贝,在赋值
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            //释放锁
            lock.unlock();
        }
    }

五、结论

  • 由第4步的方法源码解析可知:
  • CopyOnWriteArrayList读取时不加锁,只是写入、删除、修改时加锁,所以合适读数据多写数据少的场景。
  • CopyOnWriteArrayList类中所有写的操作都在新数组中完成,占用内存较高;

原文链接:https://blog.csdn.net/li1325169021/article/details/117675193



所属网站分类: 技术文章 > 博客

作者:天天在家

链接:http://www.javaheidong.com/blog/article/222574/d3c0a09717b5dc5925d8/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

10 0
收藏该文
已收藏

评论内容:(最多支持255个字符)