zoukankan      html  css  js  c++  java
  • 瞄一眼CopyOnWriteArrayList(jdk11)

    CopyOnWriteArrayList是ArrayList线程安全的变体。使用写时复制策略进行修改操作。

    与之前版本较明显的区别是,jdk11中用来保护所有设值方法(mutator)的ReentrantLock改为使用关键字synchronized。

    文档中也明确表示相比较于ReentrantLock更倾向于使用内置锁(We have a mild preference for builtin monitors over ReentrantLock when either will do.)。

    两个都是可重入独占锁,在不涉及到中断、超时等情况时,编码时使用synchronized明显比ReentrantLock优势得多。

    CopyOnWriteArrayList的成员变量:

        //锁对象
        final transient Object lock = new Object();
    
        //存储数据的数组
        private transient volatile Object[] array;
    
        final Object[] getArray() {
            return array;
        }
    
        final void setArray(Object[] a) {
            array = a;
        }

    第一个Object对象充当写时复制的锁对象,第二个volatile的array用来存放数据

    挑个构造函数看看:

     1     /**
     2      * 根据Collection迭代器返回的顺序创建包含指定集合元素的列表
     3      *
     4      * @param c 最初保存元素的集合
     5      * @throws NullPointerException 如果指定的集合为null
     6      */
     7     public CopyOnWriteArrayList(Collection<? extends E> c) {
     8         Object[] es;
     9         if (c.getClass() == CopyOnWriteArrayList.class)
    10             es = ((CopyOnWriteArrayList<?>)c).getArray();    //同类对象直接获取array赋值
    11         else {
    12             es = c.toArray();
    13             // defend against c.toArray (incorrectly) not returning Object[]
    14             // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
    15             if (es.getClass() != Object[].class)
    16                 es = Arrays.copyOf(es, es.length, Object[].class);
    17         }
    18         setArray(es);    //array赋值
    19     }

    注释中说,L15的条件分支是为了解决Collection.toArray()没有正确地返回为Object[]类型,而是错误地返回为Object类型。这个bug在jdk9已经被修复了。不知道为什么在11.0.2还留着。

    jdk中的//单行注释一般都挺有意思的…

    读操作和ArrayList没多大差别,所以都是弱一致性的。挑几个写操作看看:

     1   public void add(int index, E element) {
     2     synchronized (lock) { //获取独占锁
     3       Object[] es = getArray();
     4       int len = es.length;
     5       if (index > len || index < 0) { //越界校验
     6         throw new IndexOutOfBoundsException(outOfBounds(index, len));
     7       }
     8       Object[] newElements;
     9       int numMoved = len - index;
    10       if (numMoved == 0) {  //若index=array.length,则新元素添加在末尾
    11         newElements = Arrays.copyOf(es, len + 1);
    12       } else {
    13         newElements = new Object[len + 1];    //创建array副本,调用System.arraycopy()移动index后元素添加新元素于index
    14         System.arraycopy(es, 0, newElements, 0, index);
    15         System.arraycopy(es, index, newElements, index + 1, numMoved);
    16       }
    17       newElements[index] = element;
    18       setArray(newElements);    //将修改后的array副本回写给array
    19     }
    20   }
     1   public E set(int index, E element) {
     2     synchronized (lock) { //获取独占锁
     3       Object[] es = getArray();
     4       E oldValue = elementAt(es, index);  //找出index位置的元素
     5       if (oldValue != element) {    //如果元素与原先元素不同,则创建array副本。在副本修改后写回array
     6         es = es.clone();
     7         es[index] = element;
     8         setArray(es);
     9       }
    10       return oldValue;
    11     }
    12   }

    值得注意的是,之前版本的set方法在上段代码L9多了个分支:

    } else {
          // Not quite a no-op; ensures volatile write semantics
          setArray(elements);
    }

    注释说了,setArray()是为了保持volatile写的语义,即内存一致性:当存在并发时,将对象放入CopyOnWriteArrayList之前的线程中的操作happen-before随后通过另一线程从CopyOnWriteArrayList中访问或移除该元素的操作。由于jdk11使用synchronized替代了ReentrantLock,也就不需要这一段了。

  • 相关阅读:
    任意进制间的转换
    判断线段相交 hdu 1086
    大数(高精度)加减乘除取模运算
    sqlserver2008透明书库加密
    数据库质疑
    sql2005 和sql2008 同时安装
    editrules
    sqlserver 表值函数
    sqlserver释放内存
    sql2008查看备份进度
  • 原文地址:https://www.cnblogs.com/niceboat/p/9978910.html
Copyright © 2011-2022 走看看