zoukankan      html  css  js  c++  java
  • CopyOnWriteArrayList与Collections.synchronizedList

    1、CopyOnWriteArrayList

    1.CopyOnWriteArrayList(字译名称:写时复制),它可以看成是线程安全且读操作无锁的ArrayList。

    2.使用场景:

    读操作远远大于写操作,比如有些系统级别的信息,往往需要加载或者修改很少的次数,但是会被系统内的所有模块频繁的访问。

    3.原理:

    CopyOnWriteArrayList容器允许并发读,读操作时无锁的,性能高。写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新的副本上执行写操作(此时仍然可以读取,读取的时旧的容器中的数据),结束之后再将原容器的引用指向新容器。

    特点:这种链表,读取完全不用加锁,写入也不会阻塞读取,只有写入和写入之间需要进行同步等待。

    缺点:1)占用内存,每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC

               2)无法保证实时性,Vector对于读写操作都同步,保证了读和写的一致性,但是CopyOnWriteArrayList,写和读分别作用在新老不同的容器上,在写的过程中,读不会阻塞,但是读取到的是老容器的数据。

    CopyOnWriteArrayList在线程对其进行些操作的时候,会拷贝一个新的数组以存放新的字段。其写操作的代码如下:

    /** The lock protecting all mutators */  
        transient final ReentrantLock lock = new ReentrantLock();  
      
        /** The array, accessed only via getArray/setArray. */  
        private volatile transient Object[] array;//保证了线程的可见性  
          
         public boolean add(E e) {  
        final ReentrantLock lock = this.lock;//ReentrantLock 保证了线程的可见性和顺序性,即保证了多线程安全。  
        lock.lock();  
        try {  
            Object[] elements = getArray();  
            int len = elements.length;  
            Object[] newElements = Arrays.copyOf(elements, len + 1);//在原先数组基础之上新建长度+1的数组,并将原先数组当中的内容拷贝到新数组当中。  
            newElements[len] = e;//设值  
            setArray(newElements);//对新数组进行赋值  
            return true;  
        } finally {  
            lock.unlock();  
        }  
    }
    View Code

    2、Collections.synchronizedList

    2.1ArrayList

      ArrayList是非线性安全,此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。即在一方在便利列表,而另一方在修改列表时,会报ConcurrentModificationException错误。而这不是唯一的并发时容易发生的错误,在多线程进行插入操作时,由于没有进行同步操作,容易丢失数据。因此,在开发过程当中,ArrayList并不适用于多线程的操作。

    2.2Vector

      从JDK1.0开始,Vector便存在JDK中,Vector是一个线程安全的列表,采用数组实现。其线程安全的实现方式是对所有操作都加上了synchronized关键字,这种方式严重影响效率,因此,不再推荐使用Vector了。

    2.3Collections.synchronizedList

      CopyOnWriteArrayList和Collections.synchronizedList是实现线程安全的列表的两种方式。两种实现方式分别针对不同情况有不同的性能表现,其中CopyOnWriteArrayList的写操作性能较差,而多线程的读操作性能较好。而Collections.synchronizedList的写操作性能比CopyOnWriteArrayList在多线程操作的情况下要好很多,而读操作因为是采用了synchronized关键字的方式,其读操作性能并不如CopyOnWriteArrayList。因此在不同的应用场景下,应该选择不同的多线程安全实现类。

    Collections.synchronizedList的源码可知,其实现线程安全的方式是建立了list的包装类,代码如下:

    public static <T> List<T> synchronizedList(List<T> list) {  
    return (list instanceof RandomAccess ?  
                   new SynchronizedRandomAccessList<T>(list) :  
                   new SynchronizedList<T>(list));//根据不同的list类型最终实现不同的包装类。  
    } 
    View Code

    其中,SynchronizedList对部分操作加上了synchronized关键字以保证线程安全。但其iterator()操作还不是线程安全的。部分SynchronizedList的代码如下:

    public E get(int index) {  
            synchronized(mutex) {return list.get(index);}  
            }  
        public E set(int index, E element) {  
            synchronized(mutex) {return list.set(index, element);}  
            }  
        public void add(int index, E element) {  
            synchronized(mutex) {list.add(index, element);}  
            }  
        public ListIterator<E> listIterator() {  
            return list.listIterator(); // Must be manually synched by user 需要用户保证同步,否则仍然可能抛出ConcurrentModificationException  
            }  
      
        public ListIterator<E> listIterator(int index) {  
            return list.listIterator(index); // Must be manually synched by user <span style="font-family: Arial, Helvetica, sans-serif;">需要用户保证同步,否则仍然可能抛出ConcurrentModificationException</span>  
    }  
    View Code
    写操作:在线程数目增加时CopyOnWriteArrayList的写操作性能下降非常严重,而Collections.synchronizedList虽然有性能的降低,但下降并不明显。
    读操作:在多线程进行读时,Collections.synchronizedList和CopyOnWriteArrayList均有性能的降低,但是Collections.synchronizedList的性能降低更加显著。
     
  • 相关阅读:
    tcp流协议产生的粘包问题和解决方案
    使用fork并发处理多个client的请求和对等通信p2p
    最简单的回射客户/服务器程序、time_wait 状态
    C/S程序的一般流程和基本socket函数
    socket概述和字节序、地址转换函数
    IP数据报格式和IP地址路由
    利用ARP和ICMP协议解释ping命令
    TCP/IP协议栈与数据报封装
    从汇编角度来理解linux下多层函数调用堆栈运行状态
    read/write函数与(非)阻塞I/O的概念
  • 原文地址:https://www.cnblogs.com/yanmingyuan/p/10555138.html
Copyright © 2011-2022 走看看