zoukankan      html  css  js  c++  java
  • CopyOnWriteArrayList 源码分析

    CopyOnWriteArrayList

    CopyOnWriteArrayList 能解决什么问题?什么时候使用 CopyOnWriteArrayList?

    1)CopyOnWriteArrayList 是 ArrayList 的一个线程安全版本,其中所有可变操作(add、set等)
    都是通过对底层数组进行一次新的复制来实现的,CopyOnWriteArrayList 的写性能是很低的,
    随着元素个数的增长,写性能持续下降。
    2)CopyOnWriteArrayList 读取元素时不需要加锁处理,只有在写入操作时才需要获取独占锁,并发读取性能非常高。
    

    如何使用 CopyOnWriteArrayList?

    1)当并发读取操作远远超过并发写入操作时,才应该使用 CopyOnWriteArrayList,CopyOnWriteArrayList 读取时不需要加锁,读性能高。
    

    使用 CopyOnWriteArrayList 有什么风险?

    1)新增元素时会导致内存中出现两个相同内容的数组,数组元素越多越浪费内存,
    写入完毕后,旧的数组需要被垃圾回收,无形中给 GC 增加了压力。
    2)CopyOnWriteArrayList 只能保证最终一致性,并不能保证写入的元素立刻就能读取,
    只有当底层新数组更新后才能读取。
    

    CopyOnWriteArrayList 核心操作的实现原理?

    • 创建实例
        /**
         *  保护并发访问的锁,优先使用对象的内置监视器  (We have a mild preference
         * for builtin monitors over ReentrantLock when either will do.)
         */
        final transient Object lock = new Object();
    
        /** 底层存储元素的对象数组,只通过 getArray/setArray 访问. */
        private transient volatile Object[] array;
    
        /**
         *  创建一个空的 CopyOnWriteArrayList 实例
         */
        public CopyOnWriteArrayList() {
            setArray(new Object[0]);
        }
    
        /**
         *  基于指定的对象数组创建 CopyOnWriteArrayList 实例
         */
        public CopyOnWriteArrayList(E[] toCopyIn) {
            setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
        }
    
        /**
         *  基于指定的集合创建 CopyOnWriteArrayList 实例
         */
        public CopyOnWriteArrayList(Collection<? extends E> c) {
            Object[] elements;
            if (c.getClass() == CopyOnWriteArrayList.class) {
                elements = ((CopyOnWriteArrayList<?>)c).getArray();
            } else {
                elements = c.toArray();
                // defend against c.toArray (incorrectly) not returning Object[]
                // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
                if (elements.getClass() != Object[].class) {
                    elements = Arrays.copyOf(elements, elements.length, Object[].class);
                }
            }
            setArray(elements);
        }
    
    • 写入元素
        /**
         *  将指定的元素添加到此列表的尾部
         */
        @Override
        public boolean add(E e) {
            // 基于synchronized 实现线程安全
            synchronized (lock) {
                // 读取对象数组
                final Object[] elements = getArray();
                // 读取长度
                final int len = elements.length;
                // 执行一次数组拷贝,容量增加 1
                final Object[] newElements = Arrays.copyOf(elements, len + 1);
                // 写入尾部元素
                newElements[len] = e;
                // 写入对象数组
                setArray(newElements);
                return true;
            }
        }
    
    • 如果不存在,则写入元素
        /**
         *  如果目标元素没有在当前 CopyOnWriteArrayList 实例中,则将其添加到尾部
         */
        public boolean addIfAbsent(E e) {
            final Object[] snapshot = getArray();
            return CopyOnWriteArrayList.indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
                addIfAbsent(e, snapshot);
        }
    
        /**
         *  获取目标元素 o 在对象数组 elements 中的索引,起始查找索引为 index,结束索引为 fence-1
         */
        private static int indexOf(Object o, Object[] elements,
                int index, int fence) {
            if (o == null) {
                for (int i = index; i < fence; i++) {
                    if (elements[i] == null) {
                        return i;
                    }
                }
            } else {
                for (int i = index; i < fence; i++) {
                    if (o.equals(elements[i])) {
                        return i;
                    }
                }
            }
            return -1;
        }
    
        private boolean addIfAbsent(E e, Object[] snapshot) {
            synchronized (lock) {
                final Object[] current = getArray();
                final int len = current.length;
                // 检查元素索引期间,其他线程并发修改此 CopyOnWriteArrayList
                if (snapshot != current) {
                    // Optimize for lost race to another addXXX operation
                    final int common = Math.min(snapshot.length, len);
                    for (int i = 0; i < common; i++) {
                        // 如果指定索引处的元素发生了变更,则判断其是否和目标元素一致
                        if (current[i] != snapshot[i]
                                && Objects.equals(e, current[i])) {
                            // 其他线程已经将目标元素加入到此 CopyOnWriteArrayList 实例中,则直接返回。
                            return false;
                        }
                    }
                    // 再次检查元素是否已经存在
                    if (CopyOnWriteArrayList.indexOf(e, current, common, len) >= 0) {
                        return false;
                    }
                }
                // 写入元素到尾部
                final Object[] newElements = Arrays.copyOf(current, len + 1);
                newElements[len] = e;
                setArray(newElements);
                return true;
            }
        }
    
    • 批量将集合中的元素添加到 CopyOnWriteArrayList 的尾部
        /**
         *  批量将集合中的元素添加到 CopyOnWriteArrayList 的尾部
         */
        @Override
        public boolean addAll(Collection<? extends E> c) {
            // 读取对象数组
            final Object[] cs = c.getClass() == CopyOnWriteArrayList.class ?
                    ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
                    // 目标集合为空
                    if (cs.length == 0) {
                        return false;
                    }
                    synchronized (lock) {
                        final Object[] elements = getArray();
                        final int len = elements.length;
                        // 当前 CopyOnWriteArrayList 为空,并且集合的元素类型是 Object
                        if (len == 0 && cs.getClass() == Object[].class) {
                            // 直接写入对象数组
                            setArray(cs);
                        } else {
                            // 拷贝源数组
                            final Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                            // 拷贝新增数组
                            System.arraycopy(cs, 0, newElements, len, cs.length);
                            // 写入对象数组
                            setArray(newElements);
                        }
                        return true;
                    }
        }
    
    • 读取元素
        /**
         *  读取指定索引处的元素
         */
        @Override
        public E get(int index) {
            return CopyOnWriteArrayList.elementAt(getArray(), index);
        }
    
    • 替换指定索引处的元素,并返回旧值
        /**
         *  替换指定索引处的元素,并返回旧值
         */
        @Override
        public E set(int index, E element) {
            synchronized (lock) {
                final Object[] elements = getArray();
                final E oldValue = CopyOnWriteArrayList.elementAt(elements, index);
                // 新值和旧值不一致时才需要更新
                if (oldValue != element) {
                    final int len = elements.length;
                    final 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;
            }
        }
    
    • 移除指定索引处的元素,并返回其值
        /**
         *  移除指定索引处的元素,并返回其值
         */
        @Override
        public E remove(int index) {
            synchronized (lock) {
                final Object[] elements = getArray();
                final int len = elements.length;
                final E oldValue = CopyOnWriteArrayList.elementAt(elements, index);
                final int numMoved = len - index - 1;
                // 1)目标索引在数组尾部,则优化处理
                if (numMoved == 0) {
                    setArray(Arrays.copyOf(elements, len - 1));
                } else {
                    // 通过两次数组拷贝实现元素迁移
                    final 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;
            }
        }
    
    • 其他常用方法
        /**
         *  读取元素总数
         */
        @Override
        public int size() {
            return getArray().length;
        }
    
        /**
         *  此 CopyOnWriteArrayList 实例是否为空
         */
        @Override
        public boolean isEmpty() {
            return size() == 0;
        }
    
        /**
         *  此 CopyOnWriteArrayList 实例是否包含指定元素 o
         */
        @Override
        public boolean contains(Object o) {
            final Object[] elements = getArray();
            return CopyOnWriteArrayList.indexOf(o, elements, 0, elements.length) >= 0;
        }
    
        /**
         *  目标元素在此 CopyOnWriteArrayList 实例中第一次出现的索引,不存在时返回 -1
         */
        @Override
        public int indexOf(Object o) {
            final Object[] elements = getArray();
            return CopyOnWriteArrayList.indexOf(o, elements, 0, elements.length);
        }
    
        /**
         *  目标元素在此 CopyOnWriteArrayList 实例中最后一次出现的索引,不存在时返回 -1
         */
        @Override
        public int lastIndexOf(Object o) {
            final Object[] elements = getArray();
            return CopyOnWriteArrayList.lastIndexOf(o, elements, elements.length - 1);
        }
    
        /**
         *  读取底层的对象数组
         */
        @Override
        public Object[] toArray() {
            final Object[] elements = getArray();
            // 通过数组拷贝实现复制
            return Arrays.copyOf(elements, elements.length);
        }
    
        /**
         *  清空此 CopyOnWriteArrayList 实例
         */
        @Override
        public void clear() {
            synchronized (lock) {
                setArray(new Object[0]);
            }
        }
    
        /**
         *  顺序消费此 CopyOnWriteArrayList 实例中的每一个元素
         */
        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (final Object x : getArray()) {
                @SuppressWarnings("unchecked")
                final E e = (E) x;
                action.accept(e);
            }
        }
    
        /**
         *  对此 CopyOnWriteArrayList 实例中的元素进行排序
         * created by ZXD at 3 Dec 2018 T 20:18:28
         * @param c
         */
        @Override
        public void sort(Comparator<? super E> c) {
            synchronized (lock) {
                sort(c, 0, getArray().length);
            }
        }
    
        @SuppressWarnings("unchecked")
        void sort(Comparator<? super E> c, int i, int end) {
            final Object[] es = getArray().clone();
            Arrays.sort(es, i, end, (Comparator<Object>)c);
            setArray(es);
        }
    
  • 相关阅读:
    NYOJ 625 笨蛋的难题(二)
    NYOJ 102 次方求模
    ZJU Least Common Multiple
    ZJUOJ 1073 Round and Round We Go
    NYOJ 709 异形卵
    HDU 1279 验证角谷猜想
    BNUOJ 1015 信息战(一)——加密程序
    HDU 1202 The calculation of GPA
    "蓝桥杯“基础练习:字母图形
    "蓝桥杯“基础练习:数列特征
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10060592.html
Copyright © 2011-2022 走看看