您的位置:首页 > Web前端

快速失败(fail-fast)和安全失败(fail-safe)

2018-11-26 20:05 477 查看

快速失败(fail-fast):在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

获取一个迭代器:

ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
ist.add("c");
list.add("d");
Iterator iter = list.iterator();
while(iter.hasNext()) {
value = (String)iter.next();
System.out.print(value+", ");
}

Ctrl+A查看iterator,实现方式:

public Iterator<E> iterator() {
return new Itr();
}

这里返回了一个Itr类

private class Itr implements Iterator<E> {
int cursor;      // 返回下一个元素的索引
int lastRet = -1; // 返回最后一个元素的索引,如果没有就这样
int expectedModCount = modCount; //预期修改数

//省略其他代码。。。

/**
每次进行增删改查时比较modCount 和expectedModCount是否相等,
不相等则抛出ConcurrentModificationException产生Fail-Fast
*/
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

modCount是ArrayList继承AbstractList父类得来的值

protected transient int modCount = 0; //用来记录list的实际修改次数,每次修改modCount+1

嗯,快速失败大概就是这个意思吧。。。

那么怎么解决呢?
将ArrayList替换成java.util.concurrent包下对应的类,这时候就要用到安全失败了。

安全失败(fail-safe):采用安全失败机制的集合容器,在遍历时不是直接集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("a");
list.add("b");
ist.add("c");
list.add("d");
Iterator iter = list.iterator();
while(iter.hasNext()) {
value = (String)iter.next();
System.out.print(value+", ");
}

Ctrl+A查看iterator,实现方式:

public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}

这里返回了一个COWIterator类

static final class COWIterator<E> implements ListIterator<E> {
private COWIterator(Object[] elements, int initialCursor) {
/**
调用iterator()时将新建COWIterator,
会将元素保存在snapshot 这个拷贝的数组中,我们在对老数据进行修时,
拷贝的数据不会变
*/
cursor = initialCursor;
snapshot = elements;
}
}

比较他们的实现原理

特别注意
java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),迭代器的快速失败行为应该仅用于检测程序错误

java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

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

一部分是看别人理解得来的,一部分是自己理解的,有什么不足的地方希望可以指出来…Thanks♪(・ω・)ノ

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: