• java并发编程之美-阅读记录5


    java并发包中的并发List

    5.1CopeOnWriteArrayList

      并发包中的并发List只有CopyOnWriteArrayList,该类是一个线程安全的arraylist,对其进行的修改操作都是在底层的一个复制数组上进行的,也就是使用了写时复制策略。

      该类的结构:

    public class CopyOnWriteArrayList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        private static final long serialVersionUID = 8673264195747942595L;
        // 可重入的独占锁,用来保证对arraylist的修改操作,同一时间只有一个线程
        final transient ReentrantLock lock = new ReentrantLock();
        // 存放对象的底层数组 内存可见性
        private transient volatile Object[] array;
        // 基于硬件的原子操作了Unsafe
        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);
            }
        }
    }

    问题:

      何时初始化list,初始化list的大小是多少,list是有限大小吗?    copyonwriteArraylist是无界数组

      如何保证线程安全,比如多个线程进行读写时如何保证是线程安全的?

      如何保证迭代器遍历list时的数据一致性?

    5.2源码分析

    1、初始化

      构造函数:

        // 空的list
        public CopyOnWriteArrayList() {
            setArray(new Object[0]);
        }
        // 入参为Collection类型的集合,该构造会将集合中的元素复制到list中
        public CopyOnWriteArrayList(Collection<? extends E> c) {
            Object[] elements;
            if (c.getClass() == CopyOnWriteArrayList.class)
                elements = ((CopyOnWriteArrayList<?>)c).getArray();
            else {
                elements = c.toArray();
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elements.getClass() != Object[].class)
                    elements = Arrays.copyOf(elements, elements.length, Object[].class);
            }
            setArray(elements);
        }
       // 入参为泛型数组,将数组复制到list的底层数组中
        public CopyOnWriteArrayList(E[] toCopyIn) {
            setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
        }

    2、添加元素add方法

        // 添加元素,在list的末尾添加
        public boolean add(E e) {
            final ReentrantLock lock = this.lock;
    // 获取独占锁 lock.lock();
    try {
       // 获取copyOnWriteArrayList底层数组 Object[] elements
    = getArray(); int len = elements.length;
            // 复制一份新的数组,比原数组大一,所以CopyOnWriteArraylist是一个无界数组 Object[] newElements
    = Arrays.copyOf(elements, len + 1);
    // 将要添加的元素放到新数组的最后位置 newElements[len]
    = e;
    // 使用新数组替换原来的数组 setArray(newElements);
    return true; } finally {
    // 操作完毕后,释放独占锁 lock.unlock(); } }

    3、获取元素,此时就会产生写时复制的弱一致性问题:当线程a获取到地层数组后,但是没有执行get(Object[] a,int index)方法,此时线程b操作该集合,删除了一个元素(删除的是复制出的新数组中的数据,同时会使用新数组覆盖旧数组),name线程a继续获取执行位置的数据,此时底层数组仍然是之前没有删除数据的数组,这样就产生了弱一致性问题。

        // 直接获取底层数组指定索引位置的数据
        private E get(Object[] a, int index) {
            return (E) a[index];
        }
    
        // 获取指定索引出的值    getArray方法返回底层数组
        public E get(int index) {
            return get(getArray(), index);
        }

    4、修改指定位置的元素

    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);
                } else {
                    // Not quite a no-op; ensures volatile write semantics
                    // 旧数组重新覆盖旧数组--> 这一步虽然没有改变数组,但是为了保证volatitle语义,仍然会重新设置一次
                    setArray(elements);
                }
                return oldValue;
            } finally {
                // 释放锁
                lock.unlock();
            }
        } 

    5、删除元素

        public E remove(int index) {
            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;
                // 要移动的元素为0,则代表删除的是最后一个元素
                if (numMoved == 0)
                    setArray(Arrays.copyOf(elements, len - 1));
                else {
                    // 要删除的不是最后一个,则新建一个len-1长度的数组
                    Object[] newElements = new Object[len - 1];
                    // 同时以要删除的索引index为分界线,复制0-index,index+1 - len的元素
                    System.arraycopy(elements, 0, newElements, 0, index);
                    System.arraycopy(elements, index + 1, newElements, index,
                                     numMoved);
                    setArray(newElements);
                }
                return oldValue;
            } finally {
                lock.unlock();
            }
        }

    6、弱一致性的迭代器

      所谓弱一致性是指:返回迭代器后,其他线程对list的操作(增删改)对迭代器是不可见的。

        
        public Iterator<E> iterator() {
            return new COWIterator<E>(getArray(), 0);
        }
    
        // 可以看到迭代器中的方法,不能增删改,相应的方法会抛出异常
        static final class COWIterator<E> implements ListIterator<E> {
            /** Snapshot of the array */
            private final Object[] snapshot;
            /** Index of element to be returned by subsequent call to next.  */
            private int cursor;
    
            private COWIterator(Object[] elements, int initialCursor) {
                cursor = initialCursor;
                snapshot = elements;
            }
    
            public boolean hasNext() {
                return cursor < snapshot.length;
            }
    
            public boolean hasPrevious() {
                return cursor > 0;
            }
    
            @SuppressWarnings("unchecked")
            public E next() {
                if (! hasNext())
                    throw new NoSuchElementException();
                return (E) snapshot[cursor++];
            }
    
            @SuppressWarnings("unchecked")
            public E previous() {
                if (! hasPrevious())
                    throw new NoSuchElementException();
                return (E) snapshot[--cursor];
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor-1;
            }
    
            public void remove() {
                throw new UnsupportedOperationException();
            }
    
            public void set(E e) {
                throw new UnsupportedOperationException();
            }
    
            public void add(E e) {
                throw new UnsupportedOperationException();
            }
    
            @Override
            public void forEachRemaining(Consumer<? super E> action) {
                Objects.requireNonNull(action);
                Object[] elements = snapshot;
                final int size = elements.length;
                for (int i = cursor; i < size; i++) {
                    @SuppressWarnings("unchecked") E e = (E) elements[i];
                    action.accept(e);
                }
                cursor = size;
            }
        }

    弱一致性:

    package com.nxz.blog.otherTest;
    
    import java.util.Iterator;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class TestThread003 {
    
        private static CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
    
        /**
         * 测试CopyOnWriteArrayList的弱一致性问题
         * 如果输出结果是:test1 test2 test3 test4  则说明该类具有弱一致性
        
    */ public static void main(String[] args) throws InterruptedException { copyOnWriteArrayList.add("test1"); copyOnWriteArrayList.add("test2"); copyOnWriteArrayList.add("test3"); copyOnWriteArrayList.add("test4"); Thread t = new Thread(new Runnable() { @Override public void run() { copyOnWriteArrayList.add("runnabl1"); copyOnWriteArrayList.add("runnabl2"); copyOnWriteArrayList.add("runnabl3"); } }); Iterator<String> iterator = copyOnWriteArrayList.iterator(); // 等待线程执行完毕 t.join(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }
    结果:
    test1
    test2
    test3
    test4
  • 相关阅读:
    USACO Broken Necklace 通过了
    USACO Broken Necklace
    推荐顺序ACM
    usaco暂时无法访问
    格式
    稳定排序
    归并排序
    浅析Struts1和Struts2的Action线程安全问题
    判别式模型与生成式模型的区别
    远景能源一面
  • 原文地址:https://www.cnblogs.com/nxzblogs/p/11332231.html
Copyright © 2020-2023  润新知