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

      CopyOnWriteArrayList是一个并发集合类,它实现了List接口,它是一个线程安全类而且它实现线程安全的机制和Vector不一样。Java API文档说它是通过复制一个内部数组来实现写操作(包括add、set等等)的线程安全机制。这种方式会很耗费性能,但是在遍历操作次数远大于写操作的时候效率会很高。这种复制模式称为“snapshot style”,当调用iterator方法创建迭代器时,迭代器保留一个指向底层数组的引用,在迭代器的生命周期内,这个数组永远不会改变,并且迭代器不支持写操作,会产生UnsupportedOperationException异常。

      来看看代码:

    1 /** The lock protecting all mutators */
    2     transient final ReentrantLock lock = new ReentrantLock();
    3 
    4     /** The array, accessed only via getArray/setArray. */
    5     private volatile transient Object[] array;

    从代码中可以看出,CopyOnWriteArrayList用关键字volatile修饰了数组array,这样可以确保每次遍历list时取得的数组内容都是最新的。另外还定义了一个ReentrantLock类型的lock,用作写操作时的加锁用。

      来看看写操作的代码:

    /**
         * Replaces the element at the specified position in this list with the
         * specified element.
         *
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            Object oldValue = 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 (E)oldValue;
        } finally {
            lock.unlock();
        }
        }

    在这个set方法,首先会取得lock,lock.lock(),然后定义oldValue赋值为替换前的值,接着赋值一个数组newElements,将这个新数组的对应下标的值替换为新的值。最后调用setArray方法,把底层数组替换为新数组。最后再释放lock,lock.unlock()。如果在这中间我们调用了iterator方法获取了迭代器,那么迭代器用到的数组就不是新数组,还是老的数组,迭代器如果要取得最新的数组,则必须再调用iterator方法。

    看个例子:

     1 public static void main(String[] args) {
     2         CopyOnWriteArrayList list = new CopyOnWriteArrayList();
     3         list.add(1);
     4         list.add(2);
     5         list.add(3);
     6         Iterator it = list.iterator();
     7         list.add(4);
     8         while (it.hasNext()) {
     9             int i = (Integer) it.next();
    10             System.out.println(i);
    11         }
    12         System.out.println(list);
    13     }

    这个方法的输出是:

    1 1
    2 2
    3 3
    4 [1, 2, 3, 4]

    足以证明它们操作的不是同一个数组。

  • 相关阅读:
    Windows7下安装golang语言开发环境和revel框架
    Go实战--通过gin-gonic框架搭建restful api服务(github.com/gin-gonic/gin)
    Go语言web框架 gin
    最好的6个Go语言Web框架
    mac:Go安装和配置+GoLand安装和使用之完整教程
    Goland软件使用教程(二)
    Goland软件使用教程(一)
    http_load常见问题及解决方案
    定义类和接口
    关于 xftp 上传文件时,仅仅是上传了0字节的问题
  • 原文地址:https://www.cnblogs.com/huashui/p/3215311.html
Copyright © 2011-2022 走看看