在说Fail-Fast机制时,我们先来看一个例子
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String str : list) {
if("2".equals(str)){
list.remove(str);
}
}
}
}
运行的结果出乎我们的意料,list并没有正常删除该元素,而是抛出了一个不能修改的异常java.util.ConcurrentModificationException,诶,有趣的事情发生了。仔细查找后,发现这涉及一个机制Fail-Fast机制。
Fail-Fast机制
Fail-Fast机制是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生Fail-Fast机制。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
上一段的代码foreach有两个操作
- 判断有没有下一个元素
- 将下一个元素赋值给item
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
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];
}
foreach循环的时候会生成迭代对象,而对象中会保存一个变量expectedModCount,expectedModCount是生成迭代对象的时候期望List中修改元素的次数;List中还保存着一个成员变量modCount,modCount是修改List元素的次数。一旦expectedModCount!=expectedModCount时就会出现Fail-Fast
如何解决
在阿里巴巴Java开发手册中,提出两个解决方式
- 使用Iterator方式
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String item = iterator.next();
if("2".equals(item)){
iterator.remove();
}
}
System.out.println(list); // [1]
}
}
- 使用removeIf()方法
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.removeIf(item -> "2".equals(item));
System.out.println(list); // [1]
}
}