CopyOnWriteArrayList与Collections.synchronizedList的性能对比

Collections.synchronizedList 、CopyOnWriteArrayList、Vector介绍、源码浅析与性能对比【文末福利】 本文简要的分析了下Collections.synchronizedList 、CopyOnWriteArrayList、Vector线程安全的实现机制并对它们的读,写,迭代性能进行了对比。 阅读详情

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

1 ArrayList

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

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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的包装类,代码如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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的代码如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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.   
  14.     public ListIterator<E> listIterator(int index) {  
  15.         return list.listIterator(index); // Must be manually synched by user <span style="font-family: Arial, Helvetica, sans-serif;">需要用户保证同步,否则仍然可能抛出ConcurrentModificationException</span>  
  16.         }  

3.2 CopyOnWriteArrayList

        从字面可以知道,CopyOnWriteArrayList在线程对其进行些操作的时候,会拷贝一个新的数组以存放新的字段。其写操作的代码如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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;//保证了线程的可见性  
  6.       
  7.      public boolean add(E e) {  
  8.     final ReentrantLock lock = this.lock;//ReentrantLock 保证了线程的可见性和顺序性,即保证了多线程安全。  
  9.     lock.lock();  
  10.     try {  
  11.         Object[] elements = getArray();  
  12.         int len = elements.length;  
  13.         Object[] newElements = Arrays.copyOf(elements, len + 1);//在原先数组基础之上新建长度+1的数组,并将原先数组当中的内容拷贝到新数组当中。  
  14.         newElements[len] = e;//设值  
  15.         setArray(newElements);//对新数组进行赋值  
  16.         return true;  
  17.     } finally {  
  18.         lock.unlock();  
  19.     }  
  20.     }  
        其读操作代码如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public E get(int index) {  
  2.        return (E)(getArray()[index]);  
  3.    }  
        其没有加任何同步关键字,根据以上写操作的代码可知,其每次写操作都会进行一次数组复制操作,然后对新复制的数组进行些操作,不可能存在在同时又读写操作在同一个数组上( 不是同一个对象),而读操作并没有对数组修改,不会产生线程安全问题。Java中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。
        其中setArray()操作仅仅是对array进行引用赋值。Java中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,是一个原子操作,几乎不需要CPU时间。

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

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

        测试代码:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.yang.test;  
  2.   
  3. import org.junit.Test;  
  4.   
  5. import java.util.*;  
  6. import java.util.concurrent.*;  
  7.   
  8. /** 
  9.  * Created with IntelliJ IDEA. 
  10.  * User: yangzl2008 
  11.  * Date: 14-9-18 
  12.  * Time: 下午8:36 
  13.  * To change this template use File | Settings | File Templates. 
  14.  */  
  15. public class Test02 {  
  16.   
  17.     private int NUM = 10000;  
  18.     private int THREAD_COUNT = 16;  
  19.   
  20.     @Test  
  21.     public void testAdd() throws Exception {  
  22.         List<Integer> list1 = new CopyOnWriteArrayList<Integer>();  
  23.         List<Integer> list2 = Collections.synchronizedList(new ArrayList<Integer>());  
  24.         Vector<Integer> v  = new Vector<Integer>();  
  25.   
  26.         CountDownLatch add_countDownLatch = new CountDownLatch(THREAD_COUNT);  
  27.         ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);  
  28.   
  29.         int add_copyCostTime = 0;  
  30.         int add_synchCostTime = 0;  
  31.         for (int i = 0; i < THREAD_COUNT; i++) {  
  32.             add_copyCostTime += executor.submit(new AddTestTask(list1, add_countDownLatch)).get();  
  33.         }  
  34.         System.out.println("CopyOnWriteArrayList add method cost time is " + add_copyCostTime);  
  35.   
  36.         for (int i = 0; i < THREAD_COUNT; i++) {  
  37.             add_synchCostTime += executor.submit(new AddTestTask(list2, add_countDownLatch)).get();  
  38.         }  
  39.         System.out.println("Collections.synchronizedList add method cost time is " + add_synchCostTime);  
  40.   
  41.   
  42.     }  
  43.   
  44.     @Test  
  45.     public void testGet() throws Exception {  
  46.         List<Integer> list = initList();  
  47.   
  48.         List<Integer> list1 = new CopyOnWriteArrayList<Integer>(list);  
  49.         List<Integer> list2 = Collections.synchronizedList(list);  
  50.   
  51.         int get_copyCostTime = 0;  
  52.         int get_synchCostTime = 0;  
  53.         ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);  
  54.         CountDownLatch get_countDownLatch = new CountDownLatch(THREAD_COUNT);  
  55.         for (int i = 0; i < THREAD_COUNT; i++) {  
  56.             get_copyCostTime += executor.submit(new GetTestTask(list1, get_countDownLatch)).get();  
  57.         }  
  58.         System.out.println("CopyOnWriteArrayList add method cost time is " + get_copyCostTime);  
  59.   
  60.         for (int i = 0; i < THREAD_COUNT; i++) {  
  61.             get_synchCostTime += executor.submit(new GetTestTask(list2, get_countDownLatch)).get();  
  62.         }  
  63.         System.out.println("Collections.synchronizedList add method cost time is " + get_synchCostTime);  
  64.   
  65.     }  
  66.   
  67.   
  68.     private List<Integer> initList() {  
  69.         List<Integer> list = new ArrayList<Integer>();  
  70.         int num = new Random().nextInt(1000);  
  71.         for (int i = 0; i < NUM; i++) {  
  72.             list.add(num);  
  73.         }  
  74.         return list;  
  75.     }  
  76.   
  77.     class AddTestTask implements Callable<Integer> {  
  78.         List<Integer> list;  
  79.         CountDownLatch countDownLatch;  
  80.   
  81.         AddTestTask(List<Integer> list, CountDownLatch countDownLatch) {  
  82.             this.list = list;  
  83.             this.countDownLatch = countDownLatch;  
  84.         }  
  85.   
  86.         @Override  
  87.         public Integer call() throws Exception {  
  88.             int num = new Random().nextInt(1000);  
  89.             long start = System.currentTimeMillis();  
  90.             for (int i = 0; i < NUM; i++) {  
  91.                 list.add(num);  
  92.             }  
  93.             long end = System.currentTimeMillis();  
  94.             countDownLatch.countDown();  
  95.             return (int) (end - start);  
  96.         }  
  97.     }  
  98.   
  99.     class GetTestTask implements Callable<Integer> {  
  100.         List<Integer> list;  
  101.         CountDownLatch countDownLatch;  
  102.   
  103.         GetTestTask(List<Integer> list, CountDownLatch countDownLatch) {  
  104.             this.list = list;  
  105.             this.countDownLatch = countDownLatch;  
  106.         }  
  107.   
  108.         @Override  
  109.         public Integer call() throws Exception {  
  110.             int pos = new Random().nextInt(NUM);  
  111.             long start = System.currentTimeMillis();  
  112.             for (int i = 0; i < NUM; i++) {  
  113.                 list.get(pos);  
  114.             }  
  115.             long end = System.currentTimeMillis();  
  116.             countDownLatch.countDown();  
  117.             return (int) (end - start);  
  118.         }  
  119.     }  
  120. }  
操作结果:
 写操作读操作
 CopyOnWriteArrayList Collections.
synchronizedList
CopyOnWriteArrayList Collections.
synchronizedList
2567211
43088322
8259752823
162959364426
32--38
64--721
128--938
        写操作:在线程数目增加时CopyOnWriteArrayList的写操作性能下降非常严重,而Collections.synchronizedList虽然有性能的降低,但下降并不明显。
        读操作:在多线程进行读时,Collections.synchronizedList和CopyOnWriteArrayList均有性能的降低,但是Collections.synchronizedList的性能降低更加显著。

4 结论

        CopyOnWriteArrayList,发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主,读操作远远大于写操作的场景中使用,比如缓存。而Collections.synchronizedList则可以用在CopyOnWriteArrayList不适用,但是有需要同步列表的地方, 读写操作都比较均匀的地方。
多线程java.util.ConcurrentModificationException异常问题分析和处理 多线程java.util.ConcurrentModificationException异常问题分析和处理 在Java中,多线程和Map使使用频率最高的两兄弟了,今天突然被刚入职不久的同事问到,多线程处理Map时会偶尔报异常,直接看代码 import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.*; 阅读详情

相关推荐

DTS 数据迁移

支持关系型数据库(MySQL, Oracle, SQL Server等)、NoSQL数据库、大数据系统等。DTS数据迁移是现代化数据架构调整和云迁移过程中的重要工具,能够大大降低数据迁移的复杂度和风险。DTS (Data Transmission Service) 是一种数据迁移和同步服务,:先迁移历史数据(全量),再实时同步新增变化(增量):不同数据库类型间迁移,如Oracle到MySQL。:将单库数据迁移到分库分表架构。:将业务数据迁移到分析型数据库。:将本地数据库迁移到云数据库。

qq_41081716的博客 750

简单理解Collections.synchronizedList

大家都知道ArrayList并不是线程安全的,如果想要做到线程安全,我们可以使用 Collections.synchronizedList, 但是使用 Collections.synchronizedList后是否真的就线程安全了? 1. Collections.synchronizedList 原理 工欲善其事必先利其器,我们先来看看Collections.synchronizedList 做了什么。 从源码来看,SynchronizedList 就是在 List的操作外包加了一层synchroniz

walker 2万+

STM32F4XX软件模拟I2C实现MPU6050姿态数据采集

本文详细介绍了在STM32F4XX平台上通过软件模拟I2C驱动MPU6050传感器实现姿态数据采集的完整方案。内容涵盖软件I2C的GPIO配置、核心通信函数实现、MPU6050寄存器配置详解以及数据读取处理流程,为硬件资源受限的嵌入式应用提供了灵活的解决方案,并分享了调试技巧与常见问题处理方法。

butter的博客 678

SynchronizedList的同步问题

ArrayList是非线程安全的,在多线程中同时操作ArrayList会经常出现ConcurrentModificationException。为了解决同步问题,java提供了Collections的同步类:SynchronizedList、SynchronizedMap、SynchronizedSet等。以SynchronizedList为例,从字面意义上看,应该是线程安全的。在我们的代码中,我们

hanzengbo2017的博客 3301

dxf-viewer 查看器

dxf-viewer SDK 的示例,它是一个基于 Three.js 和 Vue3 构建的基于 WebGL 的 BIM 模型查看器。它用于查看 DWG/DXF/PDF 文件。面积测量 - 角度测量 -标记。

qq_25547755的博客 866

从零开始写一个RTSP服务器(一)RTSP协议讲解

从零开始写一个RTSP服务器系列 从零开始写一个RTSP服务器(一)不一样的RTSP协议讲解 从零开始写一个RTSP服务器(二)RTP传输H.264(待写) 从零开始写一个RTSP服务器(三)一个传输H.264的RTSP服务器(待写) 从零开始写一个RTSP服务器(四)RTP传输AAC(待写) 从零开始写一个RTSP服务器(五)一个传输AAC的RTSP服务器(待写) 从零开始写一个RTSP服务器(...

JT同学的博客 7万+

JAVA集合学习

List和Set继承了Collection接口,Collection继承了IterableObject类是所有类的根类,包括集合类,集合类中的元素通常是对象,继承了Object类中的一些基本方法,例如toString()equals()hashCode()。Collection的增强for底层就是简化版本的迭代器遍历,可以DEBUG看到过程。

瞅你咋滴。 3273

Stata极简生存分析

1. 导入数据 webuse drugtr,clear *webuse -- Use dataset from Stata website stset,clear *上一行命令导入进来的数据是“生存时间数据”,会比最原始的数据多四列,这一行命令的目的是重置为原始数据,之所以这样做,是为了显示数据处理的全部过程 codebook drug codebook studytime codebook died codebook age *codebook -- Describe data contents 2.

TOP生物信息 2336

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

列表实现有ArrayList、Vector、CopyOnWriteArrayList、Collections.synchronizedList(list)四种方式。 1 ArrayList ArrayList是非线性安全,此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove ...

weixin_30918415的博客 119

Docker 与容器技术的未来:从 OCI 标准到 eBPF 的演进

OCI 的核心目标是创建一个开放的、厂商中立的容器技术标准。这意味着,只要一个容器镜像符合 OCI 镜像格式规范,它就可以在任何符合 OCI 运行时规范的容器运行时上运行。eBPF 是一种革命性的技术,它允许在 Linux 内核中安全地运行沙盒程序,而无需修改内核源代码或加载内核模块。这些程序可以注册到内核中的各种事件点(如网络包到达、系统调用、函数调用/返回),并在事件发生时执行自定义逻辑。核心思想是:在不将数据复制到用户空间的情况下,程序可以直接在内核中处理数据,极大地提高了效率和性能。

专栏 1384

某招标公告网和vcaptcha验证码识别逆向

本文分析了某验证码系统的加密流程及破解思路。首先通过反混淆技术定位到加密函数,拆解了包含指纹函数、环境参数等组成的加密字符串。研究发现验证码生成涉及canvas指纹、环境检测等反爬机制,可通过模拟浏览器环境绕过。验证过程需要解密图片顺序,通过work2.js计算还原图片位置,再结合滑动轨迹、时间等参数生成验证请求。文章仅提供技术研究思路,强调需遵守法律法规,严禁非法破解信息系统。完整流程包括环境模拟、加密参数还原、图片处理和轨迹生成等步骤,成功率较高。

码王吴彦祖的博客 583

java的list读写性能_CopyOnWriteArrayList与Collections.synchronizedList的性能对比(转)

1 ArrayListArrayList是非线性安全,此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。即在一方在便利列表,而另一方在修改列表时,会报...

weixin_35778245的博客 1072

30. 用GPT-4自动生成测试用例的完整流程

利用GPT - 4生成测试用例面临的主要问题包括:如何准确地将软件需求转化为GPT - 4能够理解的输入提示,以确保生成的测试用例覆盖所有关键功能;如何评估生成的测试用例的有效性和充分性;如何处理生成的测试用例中的错误或不合理之处;以及如何将生成的测试用例集成到现有的测试框架中。

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 473

使用多线程往LIST添加数据 线程安全list CopyOnWriteArrayList与Collections.synchronizedList的性能对比

列表实现有ArrayList、Vector、CopyOnWriteArrayList、Collections.synchronizedList(list)四种方式。 1 ArrayList ArrayList是非线性安全,此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModific...

万事俱备,就差一个程序员了 1068

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Java 的 CopyOnWriteArrayList 和 Collections.synchronizedList 有什么区别?分别有什么优缺点?

的多维度解析,既能体现对技术细节的掌控力,又能展现架构设计中的权衡思维。建议在面试中结合白板画COW内存变化示意图,增强技术表达的说服力。考虑ConcurrentLinkedQueue或其他并发结构。CopyOnWriteArrayList+容量监控。synchronizedList+分段锁。在微服务配置中心实现中,使用。需要线程安全List?

2302_81141145的博客 1618

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

上一篇: 深入理解Java:SimpleDateFormat安全的时间格式化
下一篇: 改变postgreSQL的默认schema
zljjava
博客等级 码龄15年 155粉丝 15原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值