浅析Collections.synchronizedList实现原理及如何做到线程安全、实现线程安全2种方式CopyOnWriteArrayList与Collections.synchronizedList的读写性能对比 |
您所在的位置:网站首页 › 线程安全的列表 › 浅析Collections.synchronizedList实现原理及如何做到线程安全、实现线程安全2种方式CopyOnWriteArrayList与Collections.synchronizedList的读写性能对比 |
一、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不适用,但是有需要同步列表的地方, 读写操作都比较均匀的地方。 |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |