快速失败(fail-fast)和安全失败(fail-safe)
快速失败(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♪(・ω・)ノ
- java中的快速失败(fail-fast)与安全失败(fail-safe)
- 基础概念:fail-fast(快速失败) 与 fail-safe(安全失败) 机制有什么区别
- 快速失败(fail-fast)和安全失败(fail-safe)
- 快速失败(fail-fast)和安全失败(fail-safe)(最容易懂!!!)
- 快速失败(fail-fast)和安全失败(fail-safe)(最容易懂!!!)
- 快速失败(fail-fast)和安全失败(fail-safe)
- 快速失败(fail-fast)和安全失败(fail-safe)的区别
- fail-fast(快速失败)和fail-safe(安全失败)
- java中的快速失败(fail-fast)与安全失败(fail-safe)
- 快速失败(fail-fast)和安全失败(fail-safe)
- java中的fail-fast(快速失败)机制
- ArrayList 快速失败(fail-fast)机制实现原理 源码解析
- fail-fast快速失败机制分析
- fail-fast(快速失败/报错机制)-ConcurrentModificationException
- Java学习笔记 14 快速失败fail-fast机制
- 快速失败fail-fast机制
- Java集合(16)--快速失败机制(Fail-Fast)
- 快速失败机制--fail-fast
- Java集合框架中的快速失败(fail—fast)机制
- Java集合-05fail-fast(快速失败)机制原理及解决方法