zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    SharePoint 2013 APP 开发示例 (六)服务端跨域访问 Web Service (REST API)
    麦咖啡导致电脑不能上网
    SharePoint 2013 Central Admin 不能打开
    SharePoint 2013 APP 开发示例 (五)跨域访问 Web Service (REST API)
    SharePoint 2013 APP 开发示例 系列
    synthesize(合成) keyword in IOS
    Git Cmd
    简单的正则匹配
    Dropbox
    SQL Server Replication
  • 原文地址:https://www.cnblogs.com/nxzblogs/p/11332231.html
Copyright © 2011-2022 走看看