fail-fast机制简介
什么是fail-fast
fail-fast 机制是java集合(Collection)中的一种错误机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
这种“ 及时失败” 的迭代器井不是一种完备的处理机制,而只是“ 善意地” 捕获并发错误,因此只能作为并发问题的预警指示器。
fail-fast示例
1 | public class FailFastTest { |
以上代码会抛出
java.util.ConcurrentModificationException
异常
fail-fast的解决办法
使用java.util.concurrent.*下的工具类
深入ArrayList源码看fast-fail的原理
先放上
首先看下ArrayList中的内部类Itr的域1
2
3
4
5private class Itr implements Iterator<E> {
int cursor; //1
int lastRet = -1; //2
int expectedModCount = modCount; //3
}
- 表示迭代器下一个元素的索引;
- 表示迭代器上一个元素的索引;
- 在创建一个迭代器时,将当前ArrayList的修改次数赋值给expectedModCount保存。
在上面的示例中,我们可以看到一般的迭代过程是1
2
3
4Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
分别看下iterator()
、hasNext()
、next()
三个方法:
- iterator():没有做任何处理,不过构造时三个域会进行初始化
1 | Itr() {} |
- hasNext():判断下一个元素索引是否等于ArrayList的大小,等于说明没有元素了
1 | public boolean hasNext() { |
- 接下来重点看next()方法
1 | public E next() { |
在获取下一个元素之前,先调用checkForComodification()进行了检查,检查当前集合的修改次数是不是跟之前保存的相同,如果相同则表示没有被其他线程修改,1
2
3
4final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount:modCount 是 AbstractList 的属性值:`protected transient int modCount = 0; 他是一个修改次数计数器,实例化一个集合之后,每次修改(源码的注释成为结构性修改),比如set,add,clear等,计数器都会加1。
这里其实就是fail-fast机制的实现原理了,将修改计数器的变化与容器关联起来:首先在构造迭代器的时候,将当前的修改计数器的值保存,之后进行遍历的时候,每访问一个数据,都要检查当前集合的修改次数是否合法,如果有其他线程修改了集合,那么modCount就会被修改,当前修改计数器的值与之前保存的值(即期望值)不同,那么将抛出ConcurrentModificationException。
fail-fast与fail-safe的区别
待补充