列表实现有ArrayList、Vector、CopyOnWriteArrayList、Collections.synchronizedList(list)四种方式。

1 ArrayList

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

  1. public boolean add(E e) {
  2. ensureCapacity(size + 1);  // Increments modCount!!
  3. elementData[size++] = e;//使用了size++操作,会产生多线程数据丢失问题。
  4. return true;
  5. }

因此,在开发过程当中,ArrayList并不适用于多线程的操作。

2 Vector

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

3 Collections.synchronizedList & CopyOnWriteArrayList

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

3.1 Collections.synchronizedList

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

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

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

  1. public E get(int index) {
  2. synchronized(mutex) {return list.get(index);}
  3. }
  4. public E set(int index, E element) {
  5. synchronized(mutex) {return list.set(index, element);}
  6. }
  7. public void add(int index, E element) {
  8. synchronized(mutex) {list.add(index, element);}
  9. }
  10. public ListIterator<E> listIterator() {
  11. return list.listIterator(); // Must be manually synched by user 需要用户保证同步,否则仍然可能抛出ConcurrentModificationException
  12. }
  13. public ListIterator<E> listIterator(int index) {
  14. return list.listIterator(index); // Must be manually synched by user <span style="font-family: Arial, Helvetica, sans-serif;">需要用户保证同步,否则仍然可能抛出ConcurrentModificationException</span>
  15. }

3.2 CopyOnWriteArrayList

        从字面可以知道,CopyOnWriteArrayList在线程对其进行些操作的时候,会拷贝一个新的数组以存放新的字段。其写操作的代码如下:
  1. /** The lock protecting all mutators */
  2. transient final ReentrantLock lock = new ReentrantLock();
  3. /** The array, accessed only via getArray/setArray. */
  4. private volatile transient Object[] array;//保证了线程的可见性
  5. public boolean add(E e) {
  6. final ReentrantLock lock = this.lock;//ReentrantLock 保证了线程的可见性和顺序性,即保证了多线程安全。
  7. lock.lock();
  8. try {
  9. Object[] elements = getArray();
  10. int len = elements.length;
  11. Object[] newElements = Arrays.copyOf(elements, len + 1);//在原先数组基础之上新建长度+1的数组,并将原先数组当中的内容拷贝到新数组当中。
  12. newElements[len] = e;//设值
  13. setArray(newElements);//对新数组进行赋值
  14. return true;
  15. } finally {
  16. lock.unlock();
  17. }
  18. }

其读操作代码如下:

  1. public E get(int index) {
  2. return (E)(getArray()[index]);
  3. }

其没有加任何同步关键字,根据以上写操作的代码可知,其每次写操作都会进行一次数组复制操作,然后对新复制的数组进行些操作,不可能存在在同时又读写操作在同一个数组上(不是同一个对象),而读操作并没有对数组修改,不会产生线程安全问题。Java中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。
        其中setArray()操作仅仅是对array进行引用赋值。Java中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,是一个原子操作,几乎不需要CPU时间。

 
        在列表有更新时直接将原有的列表复制一份,并再新的列表上进行更新操作,完成后再将引用移到新的列表上。旧列表如果仍在使用中(比如遍历)则继续有效。如此一来就不会出现修改了正在使用的对象的情况(读和写分别发生在两个对象上),同时读操作也不必等待写操作的完成,免去了锁的使用加快了读取速度。

3.3 Collections.synchronizedList & CopyOnWriteArrayList在读写操作上的差距

        测试代码:
  1. package com.yang.test;
  2. import org.junit.Test;
  3. import java.util.*;
  4. import java.util.concurrent.*;
  5. /**
  6. * Created with IntelliJ IDEA.
  7. * User: yangzl2008
  8. * Date: 14-9-18
  9. * Time: 下午8:36
  10. * To change this template use File | Settings | File Templates.
  11. */
  12. public class Test02 {
  13. private int NUM = 10000;
  14. private int THREAD_COUNT = 16;
  15. @Test
  16. public void testAdd() throws Exception {
  17. List<Integer> list1 = new CopyOnWriteArrayList<Integer>();
  18. List<Integer> list2 = Collections.synchronizedList(new ArrayList<Integer>());
  19. Vector<Integer> v  = new Vector<Integer>();
  20. CountDownLatch add_countDownLatch = new CountDownLatch(THREAD_COUNT);
  21. ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
  22. int add_copyCostTime = 0;
  23. int add_synchCostTime = 0;
  24. for (int i = 0; i < THREAD_COUNT; i++) {
  25. add_copyCostTime += executor.submit(new AddTestTask(list1, add_countDownLatch)).get();
  26. }
  27. System.out.println("CopyOnWriteArrayList add method cost time is " + add_copyCostTime);
  28. for (int i = 0; i < THREAD_COUNT; i++) {
  29. add_synchCostTime += executor.submit(new AddTestTask(list2, add_countDownLatch)).get();
  30. }
  31. System.out.println("Collections.synchronizedList add method cost time is " + add_synchCostTime);
  32. }
  33. @Test
  34. public void testGet() throws Exception {
  35. List<Integer> list = initList();
  36. List<Integer> list1 = new CopyOnWriteArrayList<Integer>(list);
  37. List<Integer> list2 = Collections.synchronizedList(list);
  38. int get_copyCostTime = 0;
  39. int get_synchCostTime = 0;
  40. ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
  41. CountDownLatch get_countDownLatch = new CountDownLatch(THREAD_COUNT);
  42. for (int i = 0; i < THREAD_COUNT; i++) {
  43. get_copyCostTime += executor.submit(new GetTestTask(list1, get_countDownLatch)).get();
  44. }
  45. System.out.println("CopyOnWriteArrayList add method cost time is " + get_copyCostTime);
  46. for (int i = 0; i < THREAD_COUNT; i++) {
  47. get_synchCostTime += executor.submit(new GetTestTask(list2, get_countDownLatch)).get();
  48. }
  49. System.out.println("Collections.synchronizedList add method cost time is " + get_synchCostTime);
  50. }
  51. private List<Integer> initList() {
  52. List<Integer> list = new ArrayList<Integer>();
  53. int num = new Random().nextInt(1000);
  54. for (int i = 0; i < NUM; i++) {
  55. list.add(num);
  56. }
  57. return list;
  58. }
  59. class AddTestTask implements Callable<Integer> {
  60. List<Integer> list;
  61. CountDownLatch countDownLatch;
  62. AddTestTask(List<Integer> list, CountDownLatch countDownLatch) {
  63. this.list = list;
  64. this.countDownLatch = countDownLatch;
  65. }
  66. @Override
  67. public Integer call() throws Exception {
  68. int num = new Random().nextInt(1000);
  69. long start = System.currentTimeMillis();
  70. for (int i = 0; i < NUM; i++) {
  71. list.add(num);
  72. }
  73. long end = System.currentTimeMillis();
  74. countDownLatch.countDown();
  75. return (int) (end - start);
  76. }
  77. }
  78. class GetTestTask implements Callable<Integer> {
  79. List<Integer> list;
  80. CountDownLatch countDownLatch;
  81. GetTestTask(List<Integer> list, CountDownLatch countDownLatch) {
  82. this.list = list;
  83. this.countDownLatch = countDownLatch;
  84. }
  85. @Override
  86. public Integer call() throws Exception {
  87. int pos = new Random().nextInt(NUM);
  88. long start = System.currentTimeMillis();
  89. for (int i = 0; i < NUM; i++) {
  90. list.get(pos);
  91. }
  92. long end = System.currentTimeMillis();
  93. countDownLatch.countDown();
  94. return (int) (end - start);
  95. }
  96. }
  97. }

操作结果:

  写操作 读操作
  CopyOnWriteArrayList  Collections.
synchronizedList
CopyOnWriteArrayList  Collections.
synchronizedList
2 567 2 1 1
4 3088 3 2 2
8 25975 28 2 3
16 295936 44 2 6
32 3 8
64 7 21
128 9 38

写操作:在线程数目增加时CopyOnWriteArrayList的写操作性能下降非常严重,而Collections.synchronizedList虽然有性能的降低,但下降并不明显。

        读操作:在多线程进行读时,Collections.synchronizedList和CopyOnWriteArrayList均有性能的降低,但是Collections.synchronizedList的性能降低更加显著。

4 结论

        CopyOnWriteArrayList,发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主,读操作远远大于写操作的场景中使用,比如缓存。而Collections.synchronizedList则可以用在CopyOnWriteArrayList不适用,但是有需要同步列表的地方,读写操作都比较均匀的地方。

CopyOnWriteArrayList与Collections.synchronizedList的性能对比的更多相关文章

  1. CopyOnWriteArrayList与Collections.synchronizedList的性能对比(转)

    列表实现有ArrayList.Vector.CopyOnWriteArrayList.Collections.synchronizedList(list)四种方式. 1 ArrayList Array ...

  2. CopyOnWriteArrayList&Collections.synchronizedList()

    1.ArrayList ArrayList是非线性安全,此类的 iterator() 和 listIterator() 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remov ...

  3. Collections.synchronizedList 、CopyOnWriteArrayList、Vector介绍、源码浅析与性能对比

    ## ArrayList线程安全问题 众所周知,`ArrayList`不是线程安全的,在并发场景使用`ArrayList`可能会导致add内容为null,迭代时并发修改list内容抛`Concurre ...

  4. Collections.synchronizedList与CopyOnWriteArrayList比较

    1.单线程方式 2.多线程版本,不安全的 ArrayList 3.多线程版本,线程安全,CopyOnWriteArrayList()方式 4.多线程版本,线程安全,Collections.synchr ...

  5. ConcurrentHashMap和 CopyOnWriteArrayList提供线程安全性和可伸缩性 以及 同步的集合类 Hashtable 和 Vector Collections.synchronizedMap 和 Collections.synchronizedList 区别缺点

    ConcurrentHashMap和 CopyOnWriteArrayList提供线程安全性和可伸缩性 DougLea的 util.concurrent 包除了包含许多其他有用的并发构造块之外,还包含 ...

  6. StringBuilder和string.Format性能对比

    本文由博主(YinaPan)原创,转载请注明出处:http://www.cnblogs.com/YinaPan/p/sbformat.html StringBuilder的性能优于string.For ...

  7. 求斐波那契数列第n位的几种实现方式及性能对比(c#语言)

    在每一种编程语言里,斐波那契数列的计算方式都是一个经典的话题.它可能有很多种计算方式,例如:递归.迭代.数学公式.哪种算法最容易理解,哪种算法是性能最好的呢? 这里给大家分享一下我对它的研究和总结:下 ...

  8. Collections.synchronizedList使用

    1.SynchronizedList类具体代码: static class SynchronizedList<E> extends SynchronizedCollection<E& ...

  9. WPF DataGrid与ListView性能对比与场景选择

    开门见山的说 性能对比: 在Demo中,DataGrid与ListView默认开启虚拟化(可以理解为动态渲染,类似懒加载只渲染屏幕可以看见的地方) DataGrid渲染10列50行随机字符280ms ...

随机推荐

  1. Android线程与异步消息处理机制

    在程序开发时,对于一些比较耗时的操作,我们通常会为其开辟一个单独的线程来执行,这样可以尽可能的减少用户等待的时间.在Android中,默认情况下,所有的操作都是在主线程中进行的,这个主线程负责管理与U ...

  2. 09.13日记(2014年9月13日00:18:26)英语,bootstrap,阮一峰,

    我们这里只推荐一本语法书:台湾的旋元佑老师写的<文法俱乐部>(简体版名为<语法俱乐部>).这本书因为出版社倒闭而绝版,淘宝可以买到影印的版本. (1)学英语:奶爸的英语教室 资 ...

  3. leetcode169——Majority Element (C++)

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  4. 文件打开方式O_DSYNC、O_RSYNC、O_SYNC

    O_DSYNC: 每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新 O_RSYNC: 每个以文件描述符作为参数的read操作等待,直到所有对文件同一部分的 ...

  5. CentOS 6.4 64位 安装 jdk 6u45

    准备: 1.下载历史版本jdk 地址: http://java.sun.com/products/archive/ 下载的版本 jdk-6u45-linux-x64-rpm.bin  Linux x6 ...

  6. overflow:hidden真的失效了吗

    项目中常常有同学遇到这样的问题,现象是给元素设置了overflow:hidden,但超出容器的部分并没有被隐藏,难道是设置的hidden失效了吗? 其实看似不合理的现象背后都会有其合理的解释. 我们知 ...

  7. Python 学习之urllib模块---用于发送网络请求,获取数据(5)

    查询城市天气最后一节 需要导入上一节的结果city10.py #!/usr/bin/python# -*- coding: UTF-8 -*-import urllib.requestfrom  ci ...

  8. 基于类和redis的监控系统开发

    最近学习python运维开发,编写得一个简单的监控系统,现记录如下,仅供学习参考. 整个程序分为7个部分: 第一个部分根据监控架构设计文档架构如下: .├── m_client│   ├── conf ...

  9. python入门 第二天笔记

    程序主文件标志if __name__=="__main__": 在程序执行python 1.py 时候 程序1.py __name__ 为 main调用其他文件是,__name__ ...

  10. Python学习_列表推导和Lambda表达式

    列表推导目的是减少将一个列表转换为另一个列表时所需编写的代码量,其功能也能用列表迭代完成 1.根据要求创建列表threes_and_fives(列表值包括1到15中能够被3或者5正常的数) three ...