表象
开启2个线程操作同一个Arrylist,一个线程A添加数据同时另一个线程B获取数据。
public class FailFast {
public static List<Integer> list = new ArrayList<>();
public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
list.add(i);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for (Iterator<Integer> integerIterator = list.iterator(); integerIterator.hasNext();) {
Integer integer = integerIterator.next();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(integer);
}
}).start();
}
}
结果
结论
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
源码解析
for (Iterator<Integer> integerIterator = list.iterator(); integerIterator.hasNext();) {
//在这里获取了迭代器中的返回数据
Integer integer = integerIterator.next();
Arrylist
@SuppressWarnings("unchecked")//这个注解的意思是清除 unchecked异常检查
public E next() {
//核心逻辑
checkForComodification();
....
}
//最终抛出异常的地方
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这里对比的是2个数据 modCount != expectedModCount,如果不相等则抛出异常
expectedModCount来源
ArryList
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
....
//1 核心代码 对变量赋值
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
可以看到Arrylist内部定义了一个迭代器,在new一个迭代器的过程中,modCount的值赋值给 expectedModCount。
modCount来源
ArryList
//属性变量
protected transient int modCount = 0;
//add操作
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//remove操作
public E remove(int index) {
...
modCount++;
...
}
总结
在集合中的任何操作都会导致 modCount变量自增,而当我们声明一个迭代器的时候,他会初始化一个值expectedModCount = modCount 。当用另一个线程操作集合导致modCount自增,而expectedModCount的值不会改变,最终在查询的时候会导致 if (modCount != expectedModCount) 代码不成立从而抛出异常。