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

先回顾一下一个常识:

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将会使得正确性和简单性得以保证。

java CopyOnWriteArrayList的使用的更多相关文章

  1. Java CopyOnWriteArrayList

    1. 为什么需要 CopyOnWriteArrayList ArrayList 的内部实现是一个数组, 并且是动态扩容的, 当插入数据时, 先判断数组是否需要扩容, 如果需要扩容, 则先扩容, 再插入 ...

  2. Java CopyOnWriteArrayList分析

    CopyOnWriteArrayList是一种线程安全的ArrayList,顾名思义,它会利用写时拷贝技术,它对共享对象做仅仅读操作的时候,大家都用一个共享对象,假设有可变的操作时,就会复制一份出来, ...

  3. java.util.ConcurrentModificationException 多线程访问ArrayList引起

    http://blog.csdn.net/androiddevelop/article/details/21509345   Java ConcurrentModificationException ...

  4. JAVA Concurrent包 中的并发集合类

    我们平时写程序需要经常用到集合类,比如ArrayList.HashMap等,但是这些集合不能够实现并发运行机制,这样在服务器上运行时就会非常的消耗资源和浪费时间,并且对这些集合进行迭代的过程中不能进行 ...

  5. HowToDoInJava Java 教程·翻译完成

    原文:HowToDoInJava 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 学习资源 目录 核心 Java 教程 什 ...

  6. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  7. JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)

    1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...

  8. Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList

    概要 本章是"JUC系列"的CopyOnWriteArrayList篇.接下来,会先对CopyOnWriteArrayList进行基本介绍,然后再说明它的原理,接着通过代码去分析, ...

  9. java并发编程:并发容器之CopyOnWriteArrayList(转)

    原文:http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开大家都在共享同一个内容,当某个 ...

随机推荐

  1. 折腾了一早上的C# WPF ListView+Grid 实现图片+文字 自动换行排列 类似Windows资源管理器效果

    <ListBox Name="lstFileManager" Background ="Transparent" ItemsSource="{B ...

  2. 在Ubuntu Linux下怎样安装QQ

    最近好多人在吐槽Linux下上QQ简直就是煎熬,网页版的不方便,网上各种版本的QQ要么是功能不全.要么是界面丑到爆,要么是运行不稳定.那么这次为大家带来一个功能完整.运行稳定的wineQQ安装过程. ...

  3. CoreGraphics之CGContext绘图

    0  CGContextRef context = UIGraphicsGetCurrentContext(); 设置上下文 
1 CGContextMoveToPoint 开始画线
 2 CGCon ...

  4. r个有标志的球放进n个不同的盒子里,要求无一空盒,问有多少种不同的分配方案?

           由题意可知道r>=n,我原来想的是先取n个全排列,剩下的r-n个每个有n中选择,所以结果是n!*n^(r-n).经满神猜测,这样是会重复的.比如说,1到5个球,ABC三个盒子,ms ...

  5. TC Asia Competition

    250PT不说了.很水得一题. 500PT 给定n(<=1e18),求最大的因子,且这个因子为完全平方,假设这个因子为x那么满足x*x*y = n, 一直枚举因子到n^(1/3)就可以了. 最后 ...

  6. UVA 10896 Sending Email

    这个题目真是伤透脑筋了,一直RE,连着改了好几个版本,又是spfa,又是单调队列dijkstra+单调队列,总是不过,后来发现M开小了,双向边应该开m的两倍,悲剧啊!!!以后不管怎样,数组一定要尽量开 ...

  7. Motion on Ubuntu

    Motion is a program that monitors the video signal from one or more cameras and is able to detect if ...

  8. 字符串相似度算法(编辑距离算法 Levenshtein Distance)

    在搞验证码识别的时候需要比较字符代码的相似度用到“编辑距离算法”,关于原理和C#实现做个记录.据百度百科介绍:编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个字串 ...

  9. ANDROID_MARS学习笔记_S01原始版_004_TableLayout

    1.xml <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android ...

  10. ArcGIS Engine生成等值线(C#)

    原文:ArcGIS Engine生成等值线(C#) 本文介绍c#写的利用ArcGIS Engine生成等值线的方法.c#写的根据雨量站的降雨量值内插出降雨量等值线的功能.做几点说明:根据离散点生成等值 ...