浅析Collections.synchronizedList实现原理及如何做到线程安全、实现线程安全2种方式CopyOnWriteArrayList与Collections.synchronizedList的读写性能对比

您所在的位置:网站首页 线程安全的列表 浅析Collections.synchronizedList实现原理及如何做到线程安全、实现线程安全2种方式CopyOnWriteArrayList与Collections.synchronizedList的读写性能对比

浅析Collections.synchronizedList实现原理及如何做到线程安全、实现线程安全2种方式CopyOnWriteArrayList与Collections.synchronizedList的读写性能对比

2024-07-11 01:39:05| 来源: 网络整理| 查看: 265

一、Collections.synchronizedList 实现原理及如何做到线程安全

  大家都知道ArrayList并不是线程安全的,如果想要做到线程安全,我们可以使用 Collections.synchronizedList, 但是使用 Collections.synchronizedList后是否真的就线程安全了?

1、Collections.synchronizedList 原理

  我们先来看看Collections.synchronizedList 做了什么。

    从源码来看,SynchronizedList 就是在 List的操作外包加了一层 synchronize 同步控制。

2、加了 Collections.synchronizedList 后,为什么还需要使用 synchronized ?

首先我们看官方文档,可以发现, 当用户通过迭代器遍历返回列表时,必须手动同步:

It is imperative that the user manually synchronize on the returned list when traversing it via [Iterator]

List list = Collections.synchronizedList(new ArrayList()); ... synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }

  也就是说官方文档明确提出:对于 使用 Iterator 遍历列表时,Collections.synchronizedList 可能发生错误。

  那除了直接使用 Iterator 要加 synchronize 保证线程安全,还有什么情况会间接使用到 Iterator吗? 那就是 for each增强for循环;

  在使用 Iteratior 遍历的同时,异步修改List的结构,发现抛出了 ConcurrentModificationException 异常;

  那怎么解决呢?官方文档说的很清楚,我们在迭代器遍历返回列表时,增加手动同步处理,下面是IteratorRunnable 修改后 代码,仅仅是在外层加了 synchronized.

static class IteratorRunnable implements Runnable { private List list; public IteratorRunnable(List synchronizeList) { this.list = synchronizeList; } @Override public void run() { while(true) { synchronized (list) { for (Integer i : list) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i + ","); } } } } }

  从运行结果来看,增加了synchronized 后,不会出现ConcurrentModificationException异常了。

3、探究下for each Java的实现

  我们先来看看.class文件中for each

  看到这,我们就可以确定 ,其实JAVA中的增强for循环底层是通过iterator来实现的。

二、CopyOnWriteArrayList与Collections.synchronizedList的性能对比

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

1、ArrayList

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

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

2、Vector

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

3、Collections.synchronizedList & CopyOnWriteArrayList

  CopyOnWriteArrayList 和 Collections.synchronizedList 是实现线程安全的列表的两种方式。

  两种实现方式分别针对不同情况有不同的性能表现,其中 CopyOnWriteArrayList的写操作性能较差,而多线程的读操作性能较好。

  而Collections.synchronizedList的写操作性能比CopyOnWriteArrayList在多线程操作的情况下要好很多,而读操作因为是采用了synchronized关键字的方式,其读操作性能并不如CopyOnWriteArrayList。

  因此在不同的应用场景下,应该选择不同的多线程安全实现类。

4、Collections.synchronizedList

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

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

5、CopyOnWriteArrayList

  从字面可以知道,CopyOnWriteArrayList在线程对其进行些操作的时候,会拷贝一个新的数组以存放新的字段。

  其没有加任何同步关键字,根据以上写操作的代码可知,其每次写操作都会进行一次数组复制操作,然后对新复制的数组进行些操作,不可能存在在同时又读写操作在同一个数组上( 不是同一个对象),而读操作并没有对数组修改,不会产生线程安全问题。

  Java中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。

  其中setArray()操作仅仅是对array进行引用赋值。Java中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,是一个原子操作,几乎不需要CPU时间。

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

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

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

  结论: 

  CopyOnWriteArrayList,发生修改时候做 copy,新老版本分离,保证读的高性能,适用于以读为主,读操作远远大于写操作的场景中使用,比如缓存。

  而Collections.synchronizedList则可以用在CopyOnWriteArrayList不适用,但是有需要同步列表的地方, 读写操作都比较均匀的地方。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭