1. 说明 java的for语句增强 是java给我们提供的语法糖 ,原理是使用了迭代器 Iterator 所以for增强 可以看成是Iterator迭代器遍历
    基于jdk1.7 测试:
package com.test;

import java.util.ArrayList;
import java.util.List;

public class MyTest {
public static void main(String[] args) throws InterruptedException {
List<String> lists = new ArrayList<>();
lists.add("test1");
lists.add("test2");
lists.add("test3");
lists.add("test4");
for (String k : lists) {
if ("test2".equals(k)) {
lists.remove(k);
}
}
/*
* Iterator<String> it = lists.iterator(); while (it.hasNext()) { String k =
* it.next(); if ("测试2".equals(k)) { lists.remove(k); } }
*/
}
}

生成的class 进行反编译 反编译工具使用的是jd-gui.exe

java foreach循环 异常java.util.ConcurrentModificationException  fail-safe_抛出异常

反编译结果:

package com.test;

import java.util.ArrayList;
import java.util.List;

public class MyTest
{
public static void main(String[] paramArrayOfString)
throws InterruptedException
{
ArrayList localArrayList = new ArrayList();
localArrayList.add("test1");
localArrayList.add("test2");
localArrayList.add("test3");
localArrayList.add("test4");
for (String str : localArrayList) {
if ("test2".equals(str)) {
localArrayList.remove(str);
}
}
}
}

java foreach循环 异常java.util.ConcurrentModificationException  fail-safe_迭代器_02

  1. 为什么迭代器遍历会抛出异常呢?
//代码
public static void main(String[] args) throws InterruptedException {
List<String> lists = new ArrayList<>();
lists.add("test1");
lists.add("test2");
lists.add("test3");
lists.add("test4");
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String k = it.next();
if ("test2".equals(k)) {
lists.remove(k);
}
}
}

modCount是什么呀?

modCount是修改次数 add remove的时候 这个值就会++

//增加元素 这里只关注ensureExplicitCapacity的modCount++ 其他知识不关注
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}

ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}


//删除元素 modCount++
public E remove(int index) {
rangeCheck(index);

modCount++;
E oldValue = elementData(index);

int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work

return oldValue;
}

迭代器与modCount有什么关系呀?

ArrayList获取iterator:

//Itr 是 ArrayList的内部类
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回元素的下表
int lastRet = -1; //上一个返回的元素下表 这个属性在remove方法中会用到 如果没有值为-1 (默认)
//期待的修改数expectedModCount 赋值为modCount 这个是异常关键 当他俩不相等的时候 就会抛出异常 看方法 checkForComodification
int expectedModCount = modCount;

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
//获取下一个值
public E next() {
//检测expectedModCount 和 modCount 如果不相等就抛出异常
//也就是说在迭代器遍历的过程中 如果数组进行了remove或者add 等能使modCount 变化的操作 那!!!! 就会抛出异常 告诉用户 可能有多个线程在操作这个数组 可能会存在数据问题
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];
}

//注意! 这个remove方法 删除元素之后 还会重新 expectedModCount = modCount; 所以这个不会抛出异常
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}


//modCount != expectedModCount 抛出异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

总结:

也就是在 expectedModCount 不等于 modCount的情况下会抛异常

平时应该怎么做呢?

  1. 用上边介绍到的Iterator的自身的remove方法
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String k = it.next();
if ("test2".equals(k)) {
it.remove();
}
}
  1. 普通的for循环
for(int i=0;i<lists.size();i++) {
if("test2".equals(lists.get(i))) {
lists.remove(i);
}
}
  1. Java8提供的流操作
lists = lists.stream().filter(k -> !"test2".equals(k)).collect(Collectors.toList());
System.out.println(lists);
  1. 直接用fail-safe类 java并发包中的类
    ConcurrentLinkedDeque 这个是用的链表 没有比较modCount
public static void main(String[] args) throws InterruptedException {
ConcurrentLinkedDeque<String> lists = new ConcurrentLinkedDeque<>();
lists.add("test1");
lists.add("test2");
lists.add("test3");
lists.add("test4");
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String k = it.next();
if ("test2".equals(k)) {
it.remove();
}
}
System.out.println(lists);
}
public boolean hasNext() {
return nextItem != null;
}
public E next() {
E item = nextItem;
if (item == null) throw new NoSuchElementException();
advance();
return item;
}
private void advance() {
lastRet = nextNode;

Node<E> p = (nextNode == null) ? startNode() : nextNode(nextNode);
for (;; p = nextNode(p)) {
if (p == null) {
// p might be active end or TERMINATOR node; both are OK
nextNode = null;
nextItem = null;
break;
}
E item = p.item;
if (item != null) {
nextNode = p;
nextItem = item;
break;
}
}
}

大总结:

        我们使用的增强for循环,其实是Java提供的语法糖,其实现原理是借助Iterator进行元素的遍历。

        但是如果在遍历过程中,不通过Iterator,而是通过集合类自身的方法对集合进行添加/删除操作。那么在Iterator进行下一次的遍历时(next方法),经检测发现有一次集合的修改操作并未通过自身进行,那么可能是发生了并发被其他线程执行的,这时候就会抛出异常,来提示用户可能发生了并发修改,这就是所谓的fail-fast机制。

        当然还是有很多种方法可以解决这类问题的。比如使用普通for循环、使用Iterator进行元素删除、使用Stream的filter、使用fail-safe的类等。

特别感谢:​​https://www.jianshu.com/p/61c8750edc3d​