在上篇文章中有写到ArrayList 和Vector,期间在Add 方法中有出现modCount 变量,这个变量继承与AbstractList,解释为记录list 修改的次数,对于其理解不是太深入,因此特意记录一下。
1、快速失败:
是指在多线程访问情况下,使用迭代器遍历集合数据时,其他线程对集合有进行了修改,此时则会抛出
Concurrent Modification Exception异常。比如ArrayList 中的add()、remove(),clear()等方法。
原理:
迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果元素数量发生变化,就会改变modCount的值。每次迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount和expectedmodCount值是否相等,是的话就返回遍历;否则抛出异常ConcurrentModificationException,终止遍历。
缺点:
首先,异常的抛出条件是判断是 modCount=expectedmodCount 。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
源码分析:
在AbstractList中,有protected transient int modCount = 0 ,同时有一个私有的内部类
和一个期望的count值:int expectedModCount = modCount;
每当调用next()/remove()方法时,都会执行checkForComodification()。
可以看到,如果 modCount 不等于 expectedModCount,
则抛出ConcurrentModificationException异常,产生fail-fast事件。
避免:在删除元素时可以调用Iterator 的remove方法。
2、安全失败:
原理:
在其他线程想要修改集合数据时,将集合进行“copy”,使得其他线程修改的是“复印件”,而不影 响其他线程对原集合数据的迭代访问,从而达到安全失败的效果
缺点:
这个和明显了,一方面,拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,在遍历期间原集合发生的修改迭代器是不知道的。
场景:
java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。
总结:
这里引用下他人博客里的图片(https://blog.youkuaiyun.com/qq_42862247/article/details/84403394)