从 modCount 看 java集合 fail-fast 机制
一、背景
在常见的Java的非线程安全集合类中(如HashMap、ArrayList),经常可以在一些修改结构的操作(如Add)中看到实例变量 modCount++
,来统计集合的修改次数。
从注释也可以看出,该字段是为 fail-fast(快速失败)机制服务。
二、简介
fail-fast 机制是能立刻报告任何可能导致失败的错误检测机制。
在java集合框架中表现为:当构建迭代器时,起初expectedModCount = modCount
,当修改了该集合时,则该集合modCount++
,随后迭代器在迭代下个元素时,会发现当前的modCount
值与期望值expectedModCount
不符,便会抛出ConcurrentModificationException
异常,以避免数据不同步带来的麻烦。
注:
- 使用迭代器的remove()方法不会抛出异常,看源码可知在其中重新设置expectedModCount。
- 该机制不保证存在非同步并发修改时一定会抛出异常,只是尽最大努力去抛出该异常,因此最好不要依赖于该异常去写程序,而只是用于debug阶段。
官方注释:
Note that fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
三、两种出现场景
3.1 单线程环境
在遍历集合的过程中,调用了集合修改方法。
例:
class Test{
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList();
list.add(1); list.add(2); list.add(3);
Iterator itr = list.iterator();
while(itr.hasNext()){
System.out.println(itr.next());// 1 \n 2 \n 3
itr.remove(); //后续不会抛出异常
}
System.out.println(list.toString());// 输出:[]
list.add(1); list.add(2); list.add(3);
itr = list.iterator();
while(itr.hasNext()){
Object i = itr.next();
System.out.println(i);
list.remove(i); //后续next时会抛出异常
}
}
}
3.2 多线程环境
一个线程在遍历该集合时,另一个线程修改了该集合结构。
ArrayList<Integer> list = new ArrayList();
list.add(1); list.add(2); list.add(3);
for(Integer i: list){
new Thread(() -> {
list.add(4);
}).run();
System.out.println(i);
}
四、迭代器源码解析
以ArrayList对Iterator的实现为例:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; // 期望的modCount值即为创建迭代器时的modCount值
Itr() {}
public boolean hasNext() { // hasNext 并不抛异常
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification(); //首先检查expectedModCount是否一致
// …省略
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; //迭代器remove后不抛异常的原因,更新 expectedModCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
/** 检查 expectedModCount 与 当前 modCount是否一致,否则抛异常*/
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
参考:
https://www.geeksforgeeks.org/fail-fast-fail-safe-iterators-java/
https://blog.csdn.net/u014692324/article/details/78577130
如有帮助,不妨点下“推荐”;若有问题,欢迎指正讨论。
作者:SimpleIto
地址:https://www.cnblogs.com/simpleito/p/10901667.html
版权:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。