fail-fast 快速失败策略
- 它是Java集合(Collection)的一种错误机制。当多个线程对同一个集合进行修改(增加、删除、修改)结构操作,使用集合的迭代器iterator,会首先检测是否有对集合的并发修改,进而产生ConcurrentModificationException异常提示。所以说我们一般情况下利用迭代器遍历集合的时候不要修改元素
- 优先考虑出现异常的场景,当异常产生的时候,直接抛出异常,程序终止;
- 链接:https://www.nowcoder.com/questionTerminal/95e4f9fa513c4ef5bd6344cc3819d3f7?pos=101&mutiTagIds=570&orderByHotValue=1
来源:牛客网
如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
List<String> list = new ArrayList<>();
// modCount = 1
list.add("hello");
// modCount = 2
list.add("bit");
// modCount = 3
list.add("hello");
// modCount = 4
list.add("java");
// jcl fail-fast
// expectedModCount = 4
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if (str.equals("bit")) {
// modCount = 5
list.remove("bit");
}
System.out.println(str);
}
以上的这个代码会出现以上所说的错误ConcurrentModificationException
我们打开看一下源码,首先我们看到next方法中有checkForComodification()函数,我们点开这个函数
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
由上述源码我们可以得到当 modcount!=exceptmodCount的时候就会发生ConcurrentModificationException的异常,
- exceptionModCount:存在于内部迭代器实现,存储当前集合修改次数
- modcount:存在于Abstractlist记录List集合被修改(add、 remove方法)的次数
- 如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
- 意义:保证多线程场景下不产生脏读
fail-safe 安全失败策略
不抛出ConcurrentModificationException的集合就称为fail-safe集合。
- 保证在对任何集合结构的修改操作都基于复制修改进行的,即先copy一个新的集合对象,然后对新的集合对象进行修改,最后将新的集合对象替换掉老的集合对象(老的集合对象的地址指向新的集合对象)。
- 由于是对copy的集合进行修改,所以对原集合所做的修改不能被迭代器所检测到,所以不抛出concurrentModificationException,juc包下线程安全的集合类,也可以说java.util.concurrent包下采用的是fail-safe机制。
缺陷:
1.对集合的复制copy会产生大量的对象,造成内存空间的浪费。
2.无法保证集合迭代过程中获取的集合数据是最新的内容,迭代器不能访问到修改的内容,遍历期间原集合发生改变迭代器是不知道的。
优点:拷贝内容的优点是为了避免ConcurrentModificationException,
java.util.concurrent包下线程安全的集合类(CopyOnWriteArrayList,ConcurrentHashMap)
区别1:安全失败是基于对底层集合的拷贝,遍历时不是对集合内容进行直接访问,而是拷贝一份原有集合的内容,对拷贝的集合进行迭代遍历。快速失败,迭代器是直接对集合中的元素进行遍历
区别2:快速失败抛出特有异常,ConcurrentModicationException,安全失败是不会抛出快速失败的特有异常
区别3:java.util包下都是快速失败,java.util.concurrent包下都是安全失败