博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CopyOnWriteArrayList与Collections.synchronizedList
阅读量:4630 次
发布时间:2019-06-09

本文共 3600 字,大约阅读时间需要 12 分钟。

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 
List
synchronizedList(List
list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList
(list) : new SynchronizedList
(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
listIterator() { return list.listIterator(); // Must be manually synched by user 需要用户保证同步,否则仍然可能抛出ConcurrentModificationException } public ListIterator
listIterator(int index) { return list.listIterator(index); // Must be manually synched by user
需要用户保证同步,否则仍然可能抛出ConcurrentModificationException }
View Code
写操作:在线程数目增加时CopyOnWriteArrayList的写操作性能下降非常严重,而Collections.synchronizedList虽然有性能的降低,但下降并不明显。
读操作:在多线程进行读时,Collections.synchronizedList和CopyOnWriteArrayList均有性能的降低,但是Collections.synchronizedList的性能降低更加显著。
 
参考
  

转载于:https://www.cnblogs.com/yanmingyuan/p/10555138.html

你可能感兴趣的文章
vue2留言板
查看>>
。。。。
查看>>
Vue报错:Uncaught RangeError: Maximum call stack size exceeded
查看>>
Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法...
查看>>
react-draft-wysiwyg富文本
查看>>
echarts - 条形图grid设置距离绘图区域的距离
查看>>
JSON字符串 拼接与解析
查看>>
java-方法。(新手)
查看>>
C++中的Lambda表达式
查看>>
ajax方法参数
查看>>
json 基础
查看>>
C#合并两张表结构相同(列数和列类型都相同)的表
查看>>
sharepoint自带JS函数获取URL参数
查看>>
字符串类型String总结
查看>>
MongoDB修改器的使用1
查看>>
Apache Tomcat 7.x 概述
查看>>
as3绕过策略文件给视频截图
查看>>
C语言程序设计第一次作业
查看>>
leetcode网学习笔记(1)
查看>>
自制操作系统Antz(9)——实现内核 (下) 实现图形化界面
查看>>