cluster failfast-fast抛出什么异常

& &&&快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
悬赏2青鸟豆&|&浏览233
才能回答问题
Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常
本周答题榜
本周回答21题
本周回答20题
本周回答6题
本周回答4题
本周回答4题
本周回答4题
本周回答4题
本周回答3题
Beijing Aptech Beida Jade Bird Information Technology Co.,Ltd北大青鸟IT教育 北京阿博泰克北大青鸟信息技术有限公司 版权所有
京ICP备号-3尽可能早的抛出异常
立即失败例外状况
Fail-fast is a property of a system or module with respect to its response to failures.
A fail-fast system is designed to immediately report at its interface any failure or condition that is likely to lead to failure.
以上来源于:
Iterators with this behavior are called fail-fast iterators.
迭代器与这种行为被称为快速失败的迭代器。
If an implementation does not wish to provide fail-fast iterators, this field may be ignored.
如果实现不希望提供fail-fast特性的迭代器,该域可以被忽略。
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification.
注意迭代器的fail-fast特性不能保证,正如在非同步化修改中做保证也是不可能的。
In situations like this the current fad is to fall back on a fail-fast, fail-early culture.
It's fast too -- not only did the UI fail to stutter at our hasty swipes and screen jumps, but halted games resumed almost instantaneously.
$firstVoiceSent
- 来自原声例句
请问您想要如何调整此模块?
感谢您的反馈,我们会尽快进行适当修改!
请问您想要如何调整此模块?
感谢您的反馈,我们会尽快进行适当修改!由ArrayList来深入理解Java中的fail-fast机制
作者:pastqing
字体:[ ] 类型:转载 时间:
fail-fast俗称快速失败,是在多线程进行迭代操作时产生冲突的一种异常抛出机制,下面我们就由ArrayList来深入理解Java中的fail-fast机制.
1. fail-fast简介
“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。某个线程在对collection进行迭代时,不允许其他线程对该collection进行结构上的修改。
例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast。
迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,因此,ConcurrentModificationException应该仅用于检测 bug。
Java.util包中的所有集合类都是快速失败的,而java.util.concurrent包中的集合类都是安全失败的;
快速失败的迭代器抛出ConcurrentModificationException,而安全失败的迭代器从不抛出这个异常。
2 fail-fast示例
示例代码:(FastFailTest.java)
import java.util.*;
import java.util.concurrent.*;
* @desc java集合中Fast-Fail的测试程序。
* fast-fail事件产生的条件:当多个线程对Collection进行操作时,若其中某一个线程通过iterator去遍历集合时,该集合的内容被其他线程所改变;则会抛出ConcurrentModificationException异常。
* fast-fail解决办法:通过util.concurrent集合包下的相应类去处理,则不会产生fast-fail事件。
* 本例中,分别测试ArrayList和CopyOnWriteArrayList这两种情况。ArrayList会产生fast-fail事件,而CopyOnWriteArrayList不会产生fast-fail事件。
* (01) 使用ArrayList时,会产生fast-fail事件,抛出ConcurrentModificationException异常;定义如下:
private static List&String& list = new ArrayList&String&();
* (02) 使用时CopyOnWriteArrayList,不会产生fast-fail事件;定义如下:
private static List&String& list = new CopyOnWriteArrayList&String&();
* @author skywang
public class FastFailTest {
private static List&String& list = new ArrayList&String&();
//private static List&String& list = new CopyOnWriteArrayList&String&();
public static void main(String[] args) {
// 同时启动两个线程对list进行操作!
new ThreadOne().start();
new ThreadTwo().start();
private static void printAll() {
System.out.println("");
String value =
Iterator iter = list.iterator();
while(iter.hasNext()) {
value = (String)iter.next();
System.out.print(value+", ");
* 向list中依次添加0,1,2,3,4,5,每添加一个数之后,就通过printAll()遍历整个list
private static class ThreadOne extends Thread {
public void run() {
int i = 0;
while (i&6) {
list.add(String.valueOf(i));
printAll();
* 向list中依次添加10,11,12,13,14,15,每添加一个数之后,就通过printAll()遍历整个list
private static class ThreadTwo extends Thread {
public void run() {
int i = 10;
while (i&16) {
list.add(String.valueOf(i));
printAll();
运行该代码,抛出异常java.util.ConcurrentModificationException!即,产生fail-fast事件!
(01) FastFailTest中通过 new ThreadOne().start() 和 new ThreadTwo().start() 同时启动两个线程去操作list。
ThreadOne线程:向list中依次添加0,1,2,3,4,5。每添加一个数之后,就通过printAll()遍历整个list。
ThreadTwo线程:向list中依次添加10,11,12,13,14,15。每添加一个数之后,就通过printAll()遍历整个list。
(02) 当某一个线程遍历list的过程中,list的内容被另外一个线程所改变了;就会抛出ConcurrentModificationException异常,产生fail-fast事件。
3. fail-fast解决办法
fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。
所以,本例中只需要将ArrayList替换成java.util.concurrent包下对应的类即可。 即,将代码
private static List&String& list = new ArrayList&String&();
private static List&String& list = new CopyOnWriteArrayList&String&();
则可以解决该办法。
4. fail-fast原理
产生fail-fast事件,是通过抛出ConcurrentModificationException异常来触发的。
那么,ArrayList是如何抛出ConcurrentModificationException异常的呢?
我们知道,ConcurrentModificationException是在操作Iterator时抛出的异常。我们先看看Iterator的源码。ArrayList的Iterator是在父类AbstractList.java中实现的。代码如下:
package java.
public abstract class AbstractList&E& extends AbstractCollection&E& implements List&E& {
// AbstractList中唯一的属性
// 用来记录List修改的次数:每修改一次(添加/删除等操作),将modCount+1
protected transient int modCount = 0;
// 返回List对应迭代器。实际上,是返回Itr对象。
public Iterator&E& iterator() {
return new Itr();
// Itr是Iterator(迭代器)的实现类
private class Itr implements Iterator&E& {
int cursor = 0;
int lastRet = -1;
// 修改数的记录值。
// 每次新建Itr()对象时,都会保存新建该对象时对应的modCount;
// 以后每次遍历List中的元素的时候,都会比较expectedModCount和modCount是否相等;
// 若不相等,则抛出ConcurrentModificationException异常,产生fail-fast事件。
int expectedModCount = modC
public boolean hasNext() {
return cursor != size();
public E next() {
// 获取下一个元素之前,都会判断“新建Itr对象时保存的modCount”和“当前的modCount”是否相等;
// 若不相等,则抛出ConcurrentModificationException异常,产生fail-fast事件。
checkForComodification();
E next = get(cursor);
lastRet = cursor++;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
AbstractList.this.remove(lastRet);
if (lastRet & cursor)
lastRet = -1;
expectedModCount = modC
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
从中,我们可以发现在调用 next() 和 remove()时,都会执行 checkForComodification()。若 “modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。
要搞明白 fail-fast机制,我们就要需要理解什么时候“modCount 不等于 expectedModCount”!
从Itr类中,我们知道 expectedModCount 在创建Itr对象时,被赋值为 modCount。通过Itr,我们知道:expectedModCount不可能被修改为不等于 modCount。所以,需要考证的就是modCount何时会被修改。
接下来,我们查看ArrayList的源码,来看看modCount是如何被修改的。
package java.
public class ArrayList&E& extends AbstractList&E&
implements List&E&, RandomAccess, Cloneable, java.io.Serializable
// list中容量变化时,对应的同步函数
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.
if (minCapacity & oldCapacity) {
Object oldData[] = elementD
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity & minCapacity)
newCapacity = minC
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
// 添加元素到队列最后
public boolean add(E e) {
// 修改modCount
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] =
// 添加元素到指定的位置
public void add(int index, E element) {
if (index & size || index & 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
// 修改modCount
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] =
// 添加集合
public boolean addAll(Collection&? extends E& c) {
Object[] a = c.toArray();
int numNew = a.
// 修改modCount
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numN
return numNew != 0;
// 删除指定位置的元素
public E remove(int index) {
RangeCheck(index);
// 修改modCount
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved & 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = // Let gc do its work
return oldV
// 快速删除指定位置的元素
private void fastRemove(int index) {
// 修改modCount
modCount++;
int numMoved = size - index - 1;
if (numMoved & 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = // Let gc do its work
// 清空集合
public void clear() {
// 修改modCount
modCount++;
// Let gc do its work
for (int i = 0; i & i++)
elementData[i] =
从中,我们发现:无论是add()、remove(),还是clear(),只要涉及到修改集合中的元素个数时,都会改变modCount的值。
接下来,我们再系统的梳理一下fail-fast是怎么产生的。步骤如下:
(01) 新建了一个ArrayList,名称为arrayList。
(02) 向arrayList中添加内容。
(03) 新建一个“线程a”,并在“线程a”中通过Iterator反复的读取arrayList的值。
(04) 新建一个“线程b”,在“线程b”中删除arrayList中的一个“节点A”。
(05) 这时,就会产生有趣的事件了。
在某一时刻,“线程a”创建了arrayList的Iterator。此时“节点A”仍然存在于arrayList中,创建arrayList时,expectedModCount = modCount(假设它们此时的值为N)。
在“线程a”在遍历arrayList过程中的某一时刻,“线程b”执行了,并且“线程b”删除了arrayList中的“节点A”。“线程b”执行remove()进行删除操作时,在remove()中执行了“modCount++”,此时modCount变成了N+1!
“线程a”接着遍历,当它执行到next()函数时,调用checkForComodification()比较“expectedModCount”和“modCount”的大小;而“expectedModCount=N”,“modCount=N+1”,这样,便抛出ConcurrentModificationException异常,产生fail-fast事件。
至此,我们就完全了解了fail-fast是如何产生的!
即,当多个线程对同一个集合进行操作的时候,某线程访问集合的过程中,该集合的内容被其他线程所改变(即其它线程通过add、remove、clear等方法,改变了modCount的值);这时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
5. 解决fail-fast的原理
上面,说明了“解决fail-fast机制的办法”,也知道了“fail-fast产生的根本原因”。接下来,我们再进一步谈谈java.util.concurrent包中是如何解决fail-fast事件的。
还是以和ArrayList对应的CopyOnWriteArrayList进行说明。我们先看看CopyOnWriteArrayList的源码:
package java.util.
import java.util.*;
import java.util.concurrent.locks.*;
import sun.misc.U
public class CopyOnWriteArrayList&E&
implements List&E&, RandomAccess, Cloneable, java.io.Serializable {
// 返回集合对应的迭代器
public Iterator&E& iterator() {
return new集合类中的fast-fail实现方式都差不多,我们以最简单的ArrayList为例吧。protected transient int modCount = 0;记录的是我们对ArrayList修改的次数,比如我们调用 add(),remove()等改变数据的操作时,会将modCount++。protected transient int modCount = 0;记录的是我们对ArrayList修改的次数,比如我们调用 add(),remove()等改变数据的操作时,会将modCount++。 COWIterator&E&(getArray(), 0);
private static class COWIterator&E& implements ListIterator&E& {
private final Object[]
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialC
// 新建COWIterator时,将集合中的元素保存到一个新的拷贝数组中。
// 这样,当原始集合的数据改变,拷贝数据中的值也不会变化。
snapshot =
public boolean hasNext() {
return cursor & snapshot.
public boolean hasPrevious() {
return cursor & 0;
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
public int nextIndex() {
public int previousIndex() {
return cursor-1;
public void remove() {
throw new UnsupportedOperationException();
public void set(E e) {
throw new UnsupportedOperationException();
public void add(E e) {
throw new UnsupportedOperationException();
从中,我们可以看出:
(01) 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。
(02) ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。
(03) ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount'和‘modCount'的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!
由于HashMap(ArrayList)并不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map(这里的修改是指结构上的修改,并非指单纯修改集合内容的元素),那么将要抛出ConcurrentModificationException 即为fail-fast策略&&&
主要通过modCount域来实现,保证线程之间的可见性,modCount即为修改次数,对于HashMap(ArrayList)内容的修改就会增加这个值, 那么在迭代器的初始化过程中就会将这个值赋值给迭代器的expectedModCount
但是fail-fast行为并不能保证,因此依赖于此异常的程序的做法是错误的
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具Java面试题:快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
在当今的互联网大军中,不少人顺应时代潮流,选择了Java开发作为自己的发展方向,进行。今天,优就业的老师给大家分享一下:快速失败(fail-fast)和安全失败(fail-safe)的区别是什么(更多内容推荐:)
Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。
官方微信更多精彩,扫码关注 或微信搜索:ujiuye
官方微博更多精彩,扫码关注 或微博搜索:优就业
注:本站稿件未经许可不得转载,转载请保留出处及源文件地址。
(责任编辑:zhangjs)
关键词阅读
免费声明:本站所提供真题均来源于网友提供或网络搜集,由本站编辑整理,仅供个人研究、交流学习使用,不涉及商业盈利目的。如涉及版权问题,请联系本站管理员予以更改或删除。
优就业官方微信扫描二维码,即可与小U亲密互动
优就业官方QQ号
咨询电话(09:00-22:00)400-650-7353
IT培训友情链接
|||||||||||||||||||||||||||||||||||||
(点击一键加群)在JDK的Collection中我们时常会看到类似于这样的话:
例如,ArrayList:
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
HashMap中:
注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
在这两段话中反复地提到”快速失败”。那么何为”快速失败”机制呢?
“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
一、fail-fast示例
public class FailFastTest {
private static List&Integer& list = new ArrayList&&();
* @desc:线程one迭代list
* @Project:test
* @file:FailFastTest.java
* @Authro:chenssy
* @data:日
private static class threadOne extends Thread{
public void run() {
Iterator&Integer& iterator = list.iterator();
while(iterator.hasNext()){
int i = iterator.next();
System.out.println(&ThreadOne 遍历:& + i);
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
* @desc:当i == 3时,修改list
* @Project:test
* @file:FailFastTest.java
* @Authro:chenssy
* @data:日
private static class threadTwo extends Thread{
public void run(){
int i = 0 ;
while(i & 6){
System.out.println(&ThreadTwo run:& + i);
if(i == 3){
list.remove(i);
public static void main(String[] args) {
for(int i = 0 ; i & 10;i++){
list.add(i);
new threadOne().start();
new threadTwo().start();
运行结果:
ThreadOne 遍历:0ThreadTwo run:0ThreadTwo run:1ThreadTwo run:2ThreadTwo run:3ThreadTwo run:4ThreadTwo run:5Exception in thread &Thread-0& java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at test.ArrayListTest$threadOne.run(ArrayListTest.java:23)
二、fail-fast产生原因
通过上面的示例和讲解,我初步知道fail-fast产生的原因就在于程序在对 collection 进行迭代时,某个线程对该 collection 在结构上对其做了修改,这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。
要了解fail-fast机制,我们首先要对ConcurrentModificationException 异常有所了解。当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常。同时需要注意的是,该异常不会始终指出对象已经由不同线程并发修改,如果单线程违反了规则,同样也有可能会抛出改异常。
诚然,迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,但是快速失败操作会尽最大努力抛出ConcurrentModificationException异常,所以因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。下面我将以ArrayList为例进一步分析fail-fast产生的原因。
从前面我们知道fail-fast是在操作迭代器时产生的。现在我们来看看ArrayList中迭代器的源代码:
private class Itr implements Iterator&E& {
int lastRet = -1;
int expectedModCount = ArrayList.this.modC
public boolean hasNext() {
return (this.cursor != ArrayList.this.size);
public E next() {
checkForComodification();
/** 省略此处代码 */
public void remove() {
if (this.lastRet & 0)
throw new IllegalStateException();
checkForComodification();
/** 省略此处代码 */
final void checkForComodification() {
if (ArrayList.this.modCount == this.expectedModCount)
throw new ConcurrentModificationException();
从上面的源代码我们可以看出,迭代器在调用next()、remove()方法时都是调用checkForComodification()方法,该方法主要就是检测modCount == expectedModCount ? 若不等则抛出ConcurrentModificationException 异常,从而产生fail-fast机制。所以要弄清楚为什么会产生fail-fast机制我们就必须要用弄明白为什么modCount != expectedModCount ,他们的值在什么时候发生改变的。
expectedModCount 是在Itr中定义的:int expectedModCount = ArrayList.this.modC所以他的值是不可能会修改的,所以会变的就是modCount。modCount是在 AbstractList 中定义的,为全局变量:
protected transient int modCount = 0;
那么他什么时候因为什么原因而发生改变呢?请看ArrayList的源码:
public boolean add(E paramE) {
ensureCapacityInternal(this.size + 1);
/** 省略此处代码 */
private void ensureCapacityInternal(int paramInt) {
if (this.elementData == EMPTY_ELEMENTDATA)
paramInt = Math.max(10, paramInt);
ensureExplicitCapacity(paramInt);
private void ensureExplicitCapacity(int paramInt) {
this.modCount += 1;
//修改modCount
/** 省略此处代码 */
public boolean remove(Object paramObject) {
if (paramObject == null)
for (i = 0; i & this. ++i) {
if (this.elementData[i] != null)
fastRemove(i);
return true;
for (i = 0; i & this. ++i) {
if (!(paramObject.equals(this.elementData[i])))
fastRemove(i);
return true;
return false;
private void fastRemove(int paramInt) {
this.modCount += 1;
//修改modCount
/** 省略此处代码 */
public void clear() {
this.modCount += 1;
//修改modCount
/** 省略此处代码 */
从上面的源代码我们可以看出,ArrayList中无论add、remove、clear方法只要是涉及了改变ArrayList元素的个数的方法都会导致modCount的改变。所以我们这里可以初步判断由于expectedModCount 得值与modCount的改变不同步,导致两者之间不等从而产生fail-fast机制。知道产生fail-fast产生的根本原因了,我们可以有如下场景:
有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程启动,同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。线程A继续遍历执行next方法时,通告checkForComodification方法发现expectedModCount  = N  ,而modCount = N + 1,两者不等,这时就抛出ConcurrentModificationException 异常,从而产生fail-fast机制。
所以,直到这里我们已经完全了解了fail-fast产生的根本原因了。知道了原因就好找解决办法了。
三、fail-fast解决办法
通过前面的实例、源码分析,我想各位已经基本了解了fail-fast的机制,下面我就产生的原因提出解决方案。这里有两种解决方案:
方案一:在遍历过程中所有涉及到改变modCount值得地方全部加上synchronized或者直接使用Collections.synchronizedList,这样就可以解决。但是不推荐,因为增删造成的同步锁可能会阻塞遍历操作。
方案二:使用CopyOnWriteArrayList来替换ArrayList。推荐使用该方案。
CopyOnWriteArrayList为何物?ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。 该类产生的开销比较大,但是在两种情况下,它非常适合使用。1:在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时。2:当遍历操作的数量大大超过可变操作的数量时。遇到这两种情况使用CopyOnWriteArrayList来替代ArrayList再适合不过了。那么为什么CopyOnWriterArrayList可以替代ArrayList呢?
第一、CopyOnWriterArrayList的无论是从数据结构、定义都和ArrayList一样。它和ArrayList一样,同样是实现List接口,底层使用数组实现。在方法上也包含add、remove、clear、iterator等方法。
第二、CopyOnWriterArrayList根本就不会产生ConcurrentModificationException异常,也就是它使用迭代器完全不会产生fail-fast机制。请看:
private static class COWIterator&E& implements ListIterator&E& {
/** 省略此处代码 */
public E next() {
if (!(hasNext()))
throw new NoSuchElementException();
return this.snapshot[(this.cursor++)];
/** 省略此处代码 */
CopyOnWriterArrayList的方法根本就没有像ArrayList中使用checkForComodification方法来判断expectedModCount 与 modCount 是否相等。它为什么会这么做,凭什么可以这么做呢?我们以add方法为例:
public boolean add(E paramE) {
ReentrantLock localReentrantLock = this.
localReentrantLock.lock();
Object[] arrayOfObject1 = getArray();
int i = arrayOfObject1.
Object[] arrayOfObject2 = Arrays.copyOf(arrayOfObject1, i + 1);
arrayOfObject2[i] = paramE;
setArray(arrayOfObject2);
int j = 1;
} finally {
localReentrantLock.unlock();
final void setArray(Object[] paramArrayOfObject) {
this.array = paramArrayOfO
CopyOnWriterArrayList的add方法与ArrayList的add方法有一个最大的不同点就在于,下面三句代码:
Object[] arrayOfObject2 = Arrays.copyOf(arrayOfObject1, i + 1);arrayOfObject2[i] = paramE;setArray(arrayOfObject2);
就是这三句代码使得CopyOnWriterArrayList不会抛ConcurrentModificationException异常。他们所展现的魅力就在于copy原来的array,再在copy数组上进行add操作,这样做就完全不会影响COWIterator中的array了。
所以CopyOnWriterArrayList所代表的核心概念就是:任何对array在结构上有所改变的操作(add、remove、clear等),CopyOnWriterArrayList都会copy现有的数据,再在copy的数据上修改,这样就不会影响COWIterator中的数据了,修改完成之后改变原有数据的引用即可。同时这样造成的代价就是产生大量的对象,同时数组的copy也是相当有损耗的。
参考文档:
-----原文出自:,请尊重作者辛勤劳动成果,转载说明出处.
-----个人站点:
阅读(...) 评论()

我要回帖

更多关于 fail fast迭代器 的文章

 

随机推荐