除了加锁外,其实还有一种方式可以防止并发修改异常,这就是将读写分离技术(不是数据库上的)。

先回顾一下一个常识:

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

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

基于上面这个常识,我们再来探讨下面这个问题:

在CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过JDK1.6的Arrays.copyof()来生成一份新的数组

然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象(这里应用了常识1),这样保证了每次写都是在新的对象上(因为要保证写的一致性,这里要对各种写操作要加一把锁,JDK1.6在这里用了重入锁),

然后读的时候就是在引用的当前对象上进行读(包括get,iterator等),不存在加锁和阻塞,针对iterator使用了一个叫COWIterator的阉割版迭代器,因为不支持写操作,当获取CopyOnWriteArrayList的迭代器时,是将迭代器里的数据引用指向当前引用指向的数据对象,无论未来发生什么写操作,都不会再更改迭代器里的数据对象引用,所以迭代器也很安全(这里应用了常识2)。

CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之间也不需要加锁,读和写之间的同步处理只是在写完后通过一个简单的“=”将引用指向新的数组对象上来,这个几乎不需要时间,这样读操作就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException,所以最后得出结论:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

在你的应用中有一个列表(List),它被频繁的遍历,但是很少被修改。像“你的主页上的前十个分类,它被频繁的访问,但是每个小时通过Quartz的Job来调度更新”。

如果你使用ArrayList来作为该列表的数据结构并且不使用同步(synchronization),你可能会遇到ConcurrentModificationException,因为在你使用Quartz的Job修改该列表时,其他的代码可能正在遍历该列表。

有些开发人员可能使用Vector或Collections.synchronizedList(List<T>)的方式来解决该问题。但是这并没有效果!虽然在列表上add(),remove()和get()方法现在对线程是安全的,但遍历时仍然会抛出ConcurrentModificationException!在你遍历在列表时,你需要在该列表上使用同步,同时,在使用Quartz修改它时,也需要使用同步机制。这对性能和可扩展性来说是一个噩梦。同步需要在所有的地方出现,仅仅是因为每个小时都需要做更新。

幸运的是,这里有更好的解决方案。使用CopyOnWriteArrayList。

当列表上的一个结构修改发生时,一个新的拷贝(copy)就会被创建。这在经常发生修改的地方使用,将会很低效。遍历该列表将不会出现ConcurrentModificationException,因为该列表在遍历时将不会被做任何的修改。

另一种避免添加同步代码但可以避免并发修改问题的方式是在调度任务中构建一个新的列表,然后将原来指向到列表上的引用赋值给新的列表。在JVM中,赋值一个新的引用是原子操作。这种方式在使用旧的遍历方式(for (int i=0; i<list.size(); i++) { … list.get(i) …})时将无效(也会出错)。切换的列表中的大小将引发新的错误产生。更加糟糕的是因为改变是在不同的线程中发生的,所以还会有很多潜在的问题。使用volatile关键字可能会有所帮助,但是对列表大小的改变依然会有问题。

内存一致性和刚发生后保证了CopyOnWriteArrayList的可用性。同时,代码变得更简单,因为根本不需要使用volatile关键字或同步。更少的代码,更少的bug!

CopyOnWriteArrayList的另一个使用案例是观察者设计模式。如果事件监听器由多个不同的线程添加和移除,那么使用CopyOnWriteArrayList将会使得正确性和简单性得以保证。

转载于:https://www.cnblogs.com/cl1024cl/p/6205131.html

CopyOnWriteArrayList相关推荐

  1. java foreach delete_Java CopyOnWriteArrayList forEach()用法及代码示例

    CopyOnWriteArrayList的forEach()方法为Iterable的每个元素执行给定的操作,直到已处理完所有元素或该操作引发异常. 用法: public void forEach (C ...

  2. 从面试角度分析CopyOnWriteArrayList源码

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 注:本系列文章中用到的jdk版本均为java8 相比很多同 ...

  3. 面试官扎心一问:知道 CopyOnWriteArrayList 吗?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 写入时复制(CopyOnWrite)思想 写入时复制(Co ...

  4. JDK1.8源码分析:线程安全的CopyOnWriteArrayList与CopyOnWriteArraySet

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:服务端开发 blog.csdn.net/u01001 ...

  5. CopyOnWriteArrayList实现原理及源码分析

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操 ...

  6. List 系列 ArrayList LinkedList CopyOnWriteArrayList Queue系列 ArrayDeque ConcurrentLinkedDeque LinkedBlo

    LinkedBlockingDeque作为一种阻塞双端队列,提供了队尾删除元素和队首插入元素的阻塞方法.该类在构造时一般需要指定容量,如果不指定,则最大容量为Integer.MAX_VALUE.另外, ...

  7. (转)线程安全的CopyOnWriteArrayList介绍

    转载自:线程安全的CopyOnWriteArrayList介绍 证明CopyOnWriteArrayList是线程安全的 先写一段代码证明CopyOnWriteArrayList确实是线程安全的. 读 ...

  8. Java CopyOnWriteArrayList

    Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...

  9. 【Java并发编程】并发容器之CopyOnWriteArrayList

    问题是什么? ArrayList 在使用iterator的时候会遇到ConcurrentModificationException的异常,就是由于遍历的时候,又进行写操作. 解决办法就是:可以通过同步 ...

  10. 源码|并发一枝花之CopyOnWriteArrayList

    CopyOnWriteArrayList的设计思想非常简单,但在设计层面有一些小问题需要注意. JDK版本:oracle java 1.8.0_102 本来不想写的,但是github上CopyOnWr ...

最新文章

  1. Android如何使用so文件和Android studio中导入so
  2. wxWidgets:wxAuiManager类用法
  3. java replaceall lt,JAVA中替换字符的方法replace和replaceAll 区别
  4. MySQL 的 RowNum 实现
  5. 详解Microsoft Office Communication Server (OCS) 2007 标准版部署(上)
  6. 某公司PIX 520防火墙系统和NAT的实施
  7. python与excel-Python与Excel之间的交互
  8. pythonpid传递函数_python实现PID算法及测试的例子
  9. 字符串函数和数据类型转换函数
  10. vmware workstation 14 密钥
  11. java如何高效查询重复数据_java – jpa条件查询获取列表中的重复值
  12. java环境配好后jar文件打开闪退,无打开方式,无反应
  13. ie不能加载flash html,ie浏览器flash无法加载怎么修复_win7系统ie浏览器flash加载不了如何解决-系统城...
  14. [微信]微信小程序开发--用户昵称中带有emoji表情的处理方法
  15. FTT 海面模拟(DirectX11)
  16. Outlook邮箱配置
  17. java如何在控制台重定向_R - 控制台输出重定向不能(可靠地)从函数调用中工作...
  18. java项目开发实践经验每日总结(2014/2/22)
  19. weblogic打补丁详细步骤(linux)
  20. python的numpy教程_ROS与Python入门教程-使用numpy

热门文章

  1. 决策树(ID3、C4.5、CART、随机森林)
  2. TPC817隔离光耦使用小结
  3. 如何给微信公众号增加留言功能?
  4. 商务英语有计算机课吗,如何去学商务英语
  5. discuz gbk php在utf8,Discuz!X2 utf8升级为Discuz!X2.5 GBK 完美解决方案
  6. 中国电子束抗蚀剂市场深度研究分析报告
  7. CSR867x — 广播数据设置接口以及如何添加厂商数据
  8. php 465端口发送邮件,云服务器使用SSL加密465端口发信样例及Demo
  9. 公司邮箱、公共邮箱、工作邮箱,常用什么邮箱?
  10. shell中的$IFS变量和$*