fail-fast机制简介

什么是fail-fast

fail-fast 机制是java集合(Collection)中的一种错误机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。

这种“ 及时失败” 的迭代器井不是一种完备的处理机制,而只是“ 善意地” 捕获并发错误,因此只能作为并发问题的预警指示器。

fail-fast示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class FailFastTest {

    static final List<Integer> list = new ArrayList<>();

    public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
exec.execute(() -> {
add((int)(Math.random() * 10));
print();
});
}
} private static void add(int number) {
list.add(number);
} private static void print() {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

以上代码会抛出java.util.ConcurrentModificationException异常

fail-fast的解决办法

使用java.util.concurrent.*下的工具类

深入ArrayList源码看fast-fail的原理

先放上
首先看下ArrayList中的内部类Itr的域

1
2
3
4
5
private class Itr implements Iterator<E> {
int cursor; //1
int lastRet = -1; //2
int expectedModCount = modCount; //3
}
  1. 表示迭代器下一个元素的索引;
  2. 表示迭代器上一个元素的索引;
  3. 在创建一个迭代器时,将当前ArrayList的修改次数赋值给expectedModCount保存。

在上面的示例中,我们可以看到一般的迭代过程是

1
2
3
4
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

分别看下iterator()hasNext()next()三个方法:

  • iterator():没有做任何处理,不过构造时三个域会进行初始化
1
Itr() {}
  • hasNext():判断下一个元素索引是否等于ArrayList的大小,等于说明没有元素了
1
2
3
public boolean hasNext() {
return cursor != size;
}
  • 接下来重点看next()方法
1
2
3
4
5
6
7
8
9
10
11
public E next() {
checkForComodification(); //1
int i = cursor; //2
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];
}

在获取下一个元素之前,先调用checkForComodification()进行了检查,检查当前集合的修改次数是不是跟之前保存的相同,如果相同则表示没有被其他线程修改,

1
2
3
4
final 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-fast的机制,下面我就产生的原因提出解决方案。这里有两种解决方案:

方案一:在遍历过程中所有涉及到改变modCount值得地方全部加上synchronized或者直接使用Collections.synchronizedList,这样就可以解决。但是不推荐,因为增删造成的同步锁可能会阻塞遍历操作。

方案二:使用CopyOnWriteArrayList来替换ArrayList。推荐使用该方案。

CopyOnWriteArrayList为何物?ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。 该类产生的开销比较大,但是在两种情况下,它非常适合使用。1:在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时。2:当遍历操作的数量大大超过可变操作的数量时。遇到这两种情况使用CopyOnWriteArrayList来替代ArrayList再适合不过了。

关于CopyOnWriteArrayList请看相关文章

深入ArrayList看fast-fail机制的更多相关文章

  1. NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

    目录 一.SelectorProvider SPI 二.SelectorProvider 加载过程 2.1 SelectorProvider 加载 2.2 Windows 下 DefaultSelec ...

  2. Java集合框架中的快速失败(fail—fast)机制

      fail-fast机制,即快速失败机制,是java集合框架中的一种错误检测机制.多线程下用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加.删除),则会抛出Concurre ...

  3. Storm ack和fail机制再论

    转自:http://www.cnblogs.com/fxjwind/p/3806404.html 之前对这个的理解有些问题,今天用到有仔细梳理了一遍,记录一下 首先开启storm tracker机制的 ...

  4. 关于java中ArrayList的快速失败机制的漏洞——使用迭代器循环时删除倒数第二个元素不会报错

    一.问题描述 话不多说,先上代码: public static void main(String[] args) throws InterruptedException { List<Strin ...

  5. HDFS深度历险 之 从客户端逻辑看HDFS写入机制

    说明 除了标注之外,本文纯属原创,转载请注明出处:https://www.jianshu.com/p/ea6ef5f5b868, https://www.cnblogs.com/monkeyteng/ ...

  6. 源码看React 事件机制

    对React熟悉的同学都知道,React中的事件机制并不是原生的那一套,事件没有绑定在原生DOM上,发出的事件也是对原生事件的包装.那么这一切是怎么实现的呢? 事件注册 首先还是看我们熟悉的代码 &l ...

  7. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...

  8. 从e.getMessage()为null看Java异常机制

    问题:自定义异常触发了,但是自定义的提示信息RuntimeException却没有带过来. throw new RuntimeException("不允许插入报价主项和报价子项同时重复的记录 ...

  9. java源码学习(四)ArrayList

    ArrayList ​ ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ​ ArrayList不是线程安全的,只能用在单线程环境下, ...

随机推荐

  1. man wc

    WC(1)        User Commands/用户命令            WC(1) NAME/名称       wc - print the number of newlines, wo ...

  2. python frozenset集合(17)

    在前一篇文章中我们对 python set集合 做了详细的讲解,而本文讲解的 frozenset集合 其实和set集合类似!区别在于frozenset集合不能修改/添加/删除,其他功能和set集合一样 ...

  3. Java——容器(Map)

    [Map接口]  

  4. js解决手机键盘影响定位的问题

    // 滑动其他地方隐藏软键盘document.body.addEventListener('touchend', function(evt) { document.activeElement.blur ...

  5. PC端无论页面有没有完全撑开把footer保持在最底部(不用定位)

    最近在写项目,有的页面没有占到一屏,然后footer也就是底部就靠上了,这样很影响美观,于是在网上找了找,下面是我的成果 解决该问题的最好方法是采用CSS3提供的一种先进布局模型 :flexbox,可 ...

  6. D - Find Integer

    D - Find Integer $a^{n}+b^{n}=c^{n}$ 给定a,n求解$b,c$ 三次以上没有整数解 #include<bits/stdc++.h> using name ...

  7. es之分词器和分析器

    Elasticsearch这种全文搜索引擎,会用某种算法对建立的文档进行分析,从文档中提取出有效信息(Token) 对于es来说,有内置的分析器(Analyzer)和分词器(Tokenizer) 1: ...

  8. [CSP-S模拟测试]:简单的期望(DP)

    题目描述 从前有个变量$x$,它的初始值已给出. 你会依次执行$n$次操作,每次操作有$p\%$的概率令$x=x\times 2$,$(100−p)\%$的概率令$x=x+1$. 假设最后得到的值为$ ...

  9. 20175221曾祥杰 实验三《敏捷开发与XP实践》

    实验三<敏捷开发与XP实践> 实验报告封面 课程:Java程序设计 班级:1752班 姓名:曾祥杰 学号:20175221 指导教师:娄嘉鹏 实验日期:2019年4月30日 实验时间:13 ...

  10. Web网站安全

    一.防SQL注入 SQL注入,就是在web提交表单,请求参数的字符串中通过注入SQL命令,提交给服务器,从而让服务器执行注入的恶意的SQL命令的行为,是发生在开发程序的数据库层的安全漏洞. SQl注入 ...