前面回顾: HashMapd的存取原理你知道多少java
今天,咱们来谈谈fail-fast与fail-safe是什么以及工做机制
复制代码
fail-fast的字面意思是“快速失败”。当咱们在遍历集合元素的时候,常常会使用迭代器,但在迭代器遍历元素的过程当中,若是集合的结构被改变的话,就会抛出异常,防止继续遍历。这就是所谓的快速失败机制。微信
下面咱们来看看官方文档在HashMap这个集合中,它是怎么解释fail-fast的(以下图): 并发
意思就是说,当Iterator这个迭代器被建立后,除了迭代器自己的方法(remove)能够改变集合的结构外,其余的因素如若改变了集合的结构,都被抛出ConcurrentModificationException异常。spa
请在继续看官方的描述:线程
意思就是说:迭代器的快速失败行为是不必定可以获得保证的,通常来讲,存在非同步的并发修改时,不可能作出任何坚定的保证的。可是快速失败迭代器会作出最大的努力来抛出ConcurrentModificationException。所以,编写依赖于此异常的程序的作法是不正确的。正确的作法应该是:迭代器的快速失败行为应该仅用于检测程序中的bug.3d
稍微总结下:fail-fast,即快速失败机制,它是java集合中的一种错误检测机制,当多个线程(当个线程也是能够滴),在结构上对集合进行改变时,就有可能会产生fail-fast机制。code
这里,我解释下什么是结构上的改变。 例如集合上的插入和删除就是结构上的改变,可是,若是是对集合中某个元素进行修改的话,并非结构上的改变哦。cdn
下面,咱们来演示下在单线程的环境下,fail-fast抛出异常的实例:对象
for(int i = 10; i < 100; i++){
map.put(i, i);
}
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 20; i++){
list.add(i);
}
Iterator<Integer> it = list.iterator();
int temp = 0;
while(it.hasNext()){
if(temp == 3){
temp++;
list.remove(3);
}else{
temp++;
System.out.println(it.next());
}
}
}
复制代码
打印结果:blog
**结果分析:**由于当temp==3的时候,执行list.remove()方法,集合的结构被改变了,因此再次遍历迭代器的时候,就会抛出异常。
咱们首先先来看下源码:
**分析:**从源码咱们能够发现,迭代器在执行next()等方法的时候,都会调用checkForComodification()这个方法,查看modCount==expectedModCount?若是相等则抛出异常。
expectedModcount:这个值在对象被建立的时候就被赋予了一个固定的值modCount。也就是说这个值是不变的。也就是说,若是在迭代器遍历元素的时候,若是modCount这个值发生了改变,那么再次遍历时就会抛出异常。
何时modCount会发生改变呢?
此次就不带你们看源码了。其实当咱们对集合的元素的个数作出改变的时候,modCount的值就会被改变,若是删除,插入。但修改则不会。
若是咱们不但愿在迭代器遍历的时候由于并发等缘由,致使集合的结构被改变,进而可能抛出异常的话,咱们能够在涉及到会影响到modCount值改变的地方,加上同步锁(synchronized),或者直接使用 Collections.synchronizedList来解决。
当咱们对集合结构上作出改变的时候,fail-fast机制就会抛出异常。可是,对于采用fail-safe机制来讲,就不会抛出异常(你们估计看到safe两个字就知道了)。
这是由于,当集合的结构被改变的时候,fail-safe机制会在复制原集合的一份数据出来,而后在复制的那份数据遍历。
所以,虽然fail-safe不会抛出异常,但存在如下缺点:
复制时须要额外的空间和时间上的开销。
不能保证遍历的是最新内容。
若是文章有错的地方欢迎指正。若是习惯在微信看技术文章,想要获取更多其余资源的同窗,能够关注微信公众号 :苦逼的码农
但愿看完让你有所收获,即是我最大的回报!