您当前的位置: 首页 >  Java

小志的博客

暂无认证

  • 3浏览

    0关注

    1217博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

小志的博客 发布时间:2021-06-07 22:31:03 ,浏览量:3

目录
    • 一、CopyOnWriteArrayList的理解
    • 二、CopyOnWriteArrayList的类图
    • 三、CopyOnWriteArrayList类中常用的方法
    • 四、CopyOnWriteArrayList常用方法的原理
        • 1、 add(E e)方法
        • 2、get(int index)方法
        • 3、set(int index, E element)方法
        • 4、remove(int index) 方法
    • 五、结论

一、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 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类中所有写的操作都在新数组中完成,占用内存较高;
关注
打赏
1661269038
查看更多评论
立即登录/注册

微信扫码登录

0.0563s