为了建城,我先用英文解释一下fail-fast。在系统设计中,快速失败系统是一种在其界面上立即报告任何可能表明故障的情况的系统。快速失败系统通常旨在停止正常操作,而不是尝试继续可能存在缺陷的过程。此类设计通常会在操作中的多个点检查系统状态,因此可以及早检测到任何故障。快速失败模块的职责是检测错误,然后让系统的下一个最高级别处理它们。这意味着fail-fast是一种通用的系统设计思想。一旦检测到错误,将立即抛出异常,程序将不再执行。publicvoidtest(Wangerwanger){if(wanger==null){thrownewRuntimeException("wangercannotbeempty");}System.out.println(wanger.toString());}一旦检测到wanger为null,就会抛出异常立即抛出,让调用者决定如何处理这种情况,下一步wanger.toString()将不会被执行——避免更严重的错误。很多时候,我们把fail-fast归类为Java集合框架的一种错误检测机制,但实际上fail-fast并不是Java集合框架独有的机制。我们之所以在collectionframework中引入fail-fast,是为了让问题更容易重现。Listlist=newArrayList<>();list.add("沉默之王二");list.add("沉默之王三");list.add("一个真正有趣的程序员,有一篇文章");for(Stringstr:list){if("SilentKingII".equals(str)){list.remove(str);}}System.out.println(list);这段代码看起来不错,但是运行起来就报错了。根据错误的栈信息,我们可以定位到ArrayList的第901行代码。finalvoidcheckForComodification(){if(modCount!=expectedModCount)thrownewConcurrentModificationException();}即remove时会触发checkForComodification方法。该方法比较modCount和expectedModCount,发现两者不相等,抛出ConcurrentModificationException异常。为什么要执行checkForComodification方法?是因为for-each本质上是一个语法糖,底层是通过迭代器Iterator和while循环实现的。让我们看一下反编译后的字节码。Listlist=newArrayList();list.add("沉默之王二");list.add("沉默之王三");list.add("一个真正有趣的程序员");Iteratorvar2=list.iterator();while(var2.hasNext()){Stringstr=(String)var2.next();if("沉默之王2".equals(str)){list.remove(str);}}System.out.println(列表);我们看一下ArrayList的iterator方法:publicIteratoriterator(){returnnewItr();}内部类Itr实现了Iterator接口。privateclassItrimplementsIterator{intcursor;//indexofnextelementtoreturnintlastRet=-1;//indexoflastelementreturned;-1ifnosuchintexpectedModCount=modCount;Itr(){}publicbooleanhasNext(){returncursor!=size;}@SuppressWarnings("unchecked)ext)publicchecked();inti=cursor;Object[]elementData=ArrayList.this.elementData;if(i>=elementData.length)thrownewConcurrentModificationException();cursor=i+1;return(E)elementData[lastRet=i];}}即比如说,当执行newItr()时,expectedModCount被赋值为modCount,modCount是List的成员变量,表示集合被修改的次数。因为list之前已经执行了3次add方法。add方法调用ensureCapacityInternal方法,ensureCapacityInternal方法调用ensureExplicitCapacity方法。在ensureExplicitCapacity方法中,会执行modCount++,所以3次相加后modCount的值为3,所以newItr()后expectedModCount的值也为3。执行第一个循环时,发现“沉默之王二”等于str,于是执行list.remove(str)。remove方法调用fastRemove方法。fastRemove方法会执行modCount++privatevoidfastRemove(intindex){modCount++;intnumMoved=size-index-1;if(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);elementData[--size]=null;//cleartoletGCdoitswork}modCount值变为4,当执行到第二个循环时,会执行Itr(Stringstr=(String)var3.next();)的next方法,调用next方法checkForComodification方法。这时候expectedModCount是3,modCount是4,所以不得不抛出ConcurrentModificationException异常。其实在阿里巴巴的Java开发手册中也有提到,不要在for-each循环中对元素进行remove/add操作。请使用Iterator方法移除元素。原因其实就是我们上面分析的,由于fail-fast保护机制。那么如何正确删除元素呢?1)删除breakListlist=newArrayList<>();list.add("SilenceKingTwo");list.add("SilenceKingThree");list.add("Areallyinterestingprogrammerwithanarticle");for(Stringstr:list){if("沉默之王二".equals(str)){list.remove(str);break;}}break然后循环不再遍历意味着Iterator的next方法是不再执行,也就是说不再执行checkForComodification方法,所以不会抛出异常。但是,当List中有重复元素需要删除时,break就不适合了。2)forloopListlist=newArrayList<>();list.add("沉默之王二");list.add("沉默之王三");list.add("一篇文章太有趣了程序员");for(inti=0,n=list.size();ilist=newArrayList<>();list.add("沉默之王二");list.add("沉默王三");list.add("一个文章成员非常有趣的程序");Iteratoritr=list.iterator();while(itr.hasNext()){Stringstr=itr.next();if("沉默之王II".equals(str)){itr.remove();}}为什么要使用Iterator的remove方法来规避fail-fast保护机制?看看remove的源码就明白了。publicvoidremove(){if(lastRet<0)thrownewIllegalStateException();checkForComodification();try{ArrayList.this.remove(lastRet);cursor=lastRet;lastRet=-1;expectedModCount=modCount;}catch(IndexOutOfBoundsExceptionex){thrownewConcurrentModification(Exception);}}删除后会执行expectedModCount=modCount,保证了expectedModCount和modCount的同步。简单概括一下,fail-fast是一种保护机制,可以通过for-each循环删除集合中的元素来验证。也就是说,for-each本质上是一种语法糖,方便遍历集合,但不适合对集合中的元素进行操作(增删改查)。本文转载自微信公众号“沉默王二”,可通过以下二维码关注。转载本文请联系沉默王二公众号。