java 快速失败(fail—fast)和 安全失败(fail—safe)
程序员文章站
2022-07-14 11:25:06
...
一:快速失败(fail—fast)
在用迭代器遍历一个集合对象时,如果遍历过程(迭代器迭代)中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent ModificationException(这个异常只建议用于检测并发修改的bug)。
场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
用ArrayList来解释 (AbstractList是ArrayList父类)
大部分博客是解释modCount != expectedModCount 这里我需要特别强调这两个属性并不是位于同一个类而是
java.util.AbstractList#modCount!=java.util.AbstractList.Itr#expectedModCount
当我们对ArrayList对象进行数据修改操作的时候将会触发java.util.AbstractList#modCount 加1 ,
当我们使用迭代器遍历ArrayList对象的时候,会新建一个迭代器对象(java.util.AbstractList.Itr)
同时初始化java.util.AbstractList.Itr#expectedModCount为java.util.AbstractList#modCount
注意自此之后expectedModCount将不会再发生改变!!!,因此如果再迭代过程中修改集合数据将导致
modCount != expectedModCount,从而在迭代器next()方法中报错
二:安全失败(fail—safe)
采用安全失败机制的集合容器,在遍历时不是直接在集合内容*问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到(事实上代码里更本没得检测,连快速失败里使用的那两个核心变量都没得了),所以不会触发Concurrent Modification Exception。
缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。