fail-fast 和 fail-safe

什么是并发修改


当一个或多个线程正在遍历一个集合(Collection),此时另一个线程修改了这个集合(添加,删除或修改)就称为并发修改。

.

fail-fast(快速失败机制)


官方文档在HashMap集合中对fail-fast的解释

官方文档对fail-fast的解释

意思就是:这个迭代器(Iterator)被创建后,除了迭代器自身的方法(remove)可以改变集合的结构,其他情况改变了集合的结构,都将跑出一个ConcurrentModificationException异常。

改变集合结构才会导致 fail-fast,修改集合元素并不会

分析ArrayList源码:
ArrayList部分源码

从上面的源码,可以发现迭代器在执行next()等方法的时候,都会调用一个方法checkForComodification(),而这个方法就是检查 modCount 是否等于 expectedModCount,如果不等于就抛出ConcurrentModificationException异常。

expectedModCount 这个变量的值在对象被创建的时候就赋予了一个固定的值 modCount ,这个值是不变的,当迭代器遍历元素的时候,如果 modCount 发生了改变,那就会抛出异常。

查看源码可以发现,当对集合进行增删操作都会 modCount++

所以当我们对集合的元素的个数做出修改(添加、删除)的时候,modCount 的值就会发生改变,但对元素进行修改则不会改变 modCount 的值。

如何预防因为 fail-fast 而抛出异常

保证在并发修改的时候,对所有会影响到 modCount 发生改变的地方,加上同步锁(synchronized),或者使用同步类容器 Collections.synchronizedList

.

fail-safe(安全失败机制)


fail-safe:任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException异常。

两个问题:

  • 开销大,因为需要复制集合,产生大量的无效对象
  • 无法保证读取的数据是目前原始数据结构中的数据
分析CopyOnWriteArrayList源码
CopyOnWriteArrayList部分源码

从源码可以看到,在对集合进行添加和删除元素的时候都进行加锁,然后让当前下标的元素添加或删除,最后将原数组的地址指向新的数组,完成复制。这里涉及到CopyOnWrite机制

这样做不会出现 fail-fast ,但是对集合进行增删操作都需要加锁,影响效率。同时增加对象容量可能会导致 OOM

在遍历过程中,集合的元素并不一定是最终的元素集合,所以只能保证最终一致性。

你可能感兴趣的:(fail-fast 和 fail-safe)