Fail-Fast,fail-fast 与 fail-safe 机制有什么区别
我们经常在JDK的收藏中看到这样的东西:
如何解决写爬虫IP受阻的问题?立即使用。
例如,ArrayList:
注意迭代器的快速失效行为是无法保证的,因为一般来说,对于是否会出现不同步的并发修改是无法做出任何硬性保证的。快速迭代器将用完。
尽力抛出一个ConcurrentModificationException。所以,为了提高这种迭代器的正确性而写一个依赖这个异常的程序是错误的:迭代
代理的快速失败行为应该仅用于检测错误。在HashMap中:
注意,迭代器的快速失败行为是不能保证的。一般来说,当存在不同步的并发修改时,不可能做出任何确定的保证。快速迭代器最大失败。
尝试引发concurrentmodification异常。因此,编写依赖于这个异常的程序是错误的。正确的方法是迭代器的快速失败行为应该
这仅用于检测程序错误。在这两段文字中,反复提到了“速衰”。那么什么是“快速失效”机制呢?
“快速失败”也称为fail-fast,是Java集合的一种错误检测机制。当多个线程对集合进行结构更改时,可能会产生快速失效机制。记住有可能,不一定。例如,假设有两个线程(线程1和线程2),线程1正在通过迭代器遍历集合A中的元素,在某个时刻线程2修改了集合A的结构(修改在结构之上,而不是简单地修改集合中元素的内容),那么程序此时会抛出ConcurrentModificationException异常,产生fail-fast机制。
一、fail-fast示例
公共类FailFastTest {
private static list integer list=new ArrayList();
/**
* @desc:线程一迭代列表
* @Project:test
* @file:FailFastTest.java
* @Authro:chenssy
* @数据:2014年7月26日
*/
私有静态类threadOne扩展线程{
公共无效运行(){
iterator integer iterator=list . iterator();
while(iterator.hasNext()){
int I=iterator . next();
System.out.println(ThreadOne遍历: I );
尝试{
Thread.sleep(十);
} catch (InterruptedException e) {
e . printstacktrace();
}
}
}
}
/**
* @desc:当i==3时,修改列表
* @Project:test
* @file:FailFastTest.java
* @Authro:chenssy
* @数据:2014年7月26日
*/
私有静态类threadTwo扩展了Thread{
公共无效运行(){
int I=0;
while(i 6){
system . out . println( thread two run: I );
if(i==3){
list . remove(I);
}
我;
}
}
}
公共静态void main(String[] args) {
for(int I=0;i 10i ){
list . add(I);
}
新的threadOne()。start();
新线程二()。start();
}
}运行结果:
ThreadOne遍历:0
ThreadTwo运行:0
线程二运行:1
线程二运行:2
线程二运行:3
线程二运行:4
线程二运行:5
线程“Thread-0”Java . util . concurrentmodificationexception中出现异常
at Java . util . ArrayListitr . checkforcomodiformation(未知来源)
at Java . util . ArrayListitr . next(未知来源)
测试时。ArrayList test $ threadone . run(ArrayList test . Java:23二、fail-fast产生原因
通过上面的例子和说明,我初步知道fail-fast的原因是程序在对集合进行迭代时,一个线程修改了集合的结构,然后迭代器会抛出ConcurrentmodificationException的异常信息,导致fail-fast。
要了解快速失效机制,我们首先要对ConcurrentModificationException异常有所了解。当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常。同时需要注意的是,该异常不会始终指出对象已经由不同线程并发修改,如果单线程违反了规则,同样也有可能会抛出改异常。
诚然,迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,但是快速失败操作会尽最大努力抛出ConcurrentModificationException异常,所以因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException应该仅用于检测臭虫。下面我将以数组列表为例进一步分析快速失效产生的原因。
从前面我们知道快速失效是在操作迭代器时产生的。现在我们来看看数组列表中迭代器的源代码:
私有类伊曲康唑实现迭代器{
(同Internationalorganizations)国际组织游标;
int lastRet=-1;
int expectedModCount=ArrayList。这个。modcount
public boolean hasNext() {
return (this.cursor!=ArrayList。这个。尺寸);
}
public E next() {
checkforcomedification();
/** 省略此处代码*/
}
公共无效删除(){
if (this.lastRet 0)
抛出新的IllegalStateException();
checkforcomedification();
/** 省略此处代码*/
}
针对修改()的最终无效检查{
if(数组列表。这个。modcount==this。expectedmodcount)
返回;
抛出new ConcurrentModificationException();
}
}从上面的源代码我们可以看出,迭代器在调用下一步()、移除()方法时都是调用checkForComodification()方法,该方法主要就是检测modCount==expectedModCount?若不等则抛出ConcurrentModificationException异常,从而产生快速失效机制。所以要弄清楚为什么会产生快速失效机制我们就必须要用弄明白为什么modCount!=expectedModCount,他们的值在什么时候发生改变的。
预期模式计数是在伊曲康唑中定义的:int expectedModCount=ArrayList。这个。mod计数;所以他的值是不可能会修改的,所以会变的就是modCount。modCount是在抽象主义者中定义的,为全局变量:
受保护的瞬态int mod count=0;那么他什么时候因为什么原因而发生改变呢?请看数组列表的源码:
公共布尔加法(E参数){
ensureCapacityInternal(这个。尺寸1);
/** 省略此处代码*/
}
private void ensureCapacityInternal(int paramInt){
如果(这个。元素数据==空_元素数据)
paramInt=Math.max(10,param int);
ensureExplicitCapacity(paramInt);
}
private void ensureExplicitCapacity(int paramInt){
这个。mod计数=1;//修改modCount
/** 省略此处代码*/
}
public boolean remove(对象参数对象){
int I;
if (paramObject==null)
for(I=0;我这个尺寸;i) {
if (this.elementData[i]!=空)
继续;
快速移除(I);
返回真实的
}
其他
for(I=0;我这个尺寸;i) {
如果(!(参数对象。等于(这个。元素数据[I])
继续;
快速移除(I);
返回真实的
}
返回错误的
}
私有void快速删除(int paramInt){
这个。mod计数=1;//修改modCount
/** 省略此处代码*/
}
公共void clear() {
这个。mod计数=1;//修改modCount
/** 省略此处代码*/
}从上面的源代码我们可以看到,ArrayList中任何涉及到改变ArrayList元素个数的add、remove、clear方法都会导致modCount的改变。所以我们在这里可以初步判断,expectedmodCount的值与modCount的变化不同步,导致两者不相等,产生fail-fast机制。知道了故障快速的根本原因,我们可以有以下场景:
有两个线程(线程A和线程B),其中线程A负责遍历列表,线程B修改列表。在线程A遍历list进程的某个时刻(此时,expectedmodCount=modCount=N),线程启动,而线程B增加一个元素,这个元素就是modCount的值的变化(modCount 1=N ^ 1)。当线程A继续遍历下一个方法的执行时,通知checkForComodification方法expectedModCount=N,但modCount=N ^ 1,两者互不相同。此时,它会抛出一个concurrentmodificationexception异常,从而产生一个快速失败机制。
所以,到现在为止,我们已经完全理解了fail-fast的根本原因。知道原因,更容易找到解决办法。
三、fail-fast解决办法
通过前面的例子和源代码分析,我想你已经基本了解fail-fast的机制了。在这里,我将提出一个解决的原因。这里有两种解决方案:
方案一:在遍历过程中所有涉及改变modCount值的地方直接添加synchronized或者使用Collections.synchronizedList,可以解决。但是不建议这样做,因为添加或删除导致的同步锁可能会阻塞遍历操作。
方案二:用CopyOnWriteArrayList替换ArrayList。推荐这个方案。这就是快速失效机制的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。