集合的并发修改问题
集合的并发修改问题
CAMELLIA!!! note 目录
集合的并发修改问题
在 Java 中,集合在迭代时可以通过集合自身的方法(如 remove
方法)或者通过迭代器的 remove
方法来删除元素。这两种删除元素的方法在使用上有明显的区别,尤其是在迭代过程中。
一、集合删除元素和迭代器删除元素的区分
1.1 使用集合方法删除元素
当你在遍历集合时,直接使用集合的 remove
方法来删除元素会导致 ConcurrentModificationException
。这是因为集合的 remove
方法会改变集合的结构,而在迭代过程中,修改集合的结构会使迭代器失效。
1 |
|
使用集合删除元素时,迭代器并不知情。
1.2 使用迭代器删除元素
相比之下,使用迭代器的 remove
方法在迭代过程中删除元素是安全的。迭代器维护了一个内部的状态来跟踪集合的结构修改,因此可以正确处理删除操作。
1 |
|
1.3 区别总结
并发修改检查:
- 集合方法:在迭代过程中使用集合的
remove
方法会导致ConcurrentModificationException
。 - 迭代器方法:迭代器的
remove
方法在迭代过程中删除元素是安全的,因为它处理了并发修改的情况。
- 集合方法:在迭代过程中使用集合的
操作便捷性:
- 集合方法:直接调用集合的方法可能更直观和简单,但在迭代过程中不安全。
- 迭代器方法:使用迭代器的
remove
方法需要额外创建迭代器对象,但它能保证在迭代过程中安全删除元素。
一致性:
- 集合方法:直接修改集合结构会导致迭代器状态不一致。
- 迭代器方法:迭代器的
remove
方法确保了迭代器和集合的一致性。
二、fail-fast 机制
fail-fast机制又被称为:快速失败机制。也就是说只要程序发现了程序对集合进行了并发修改。就会立即让其失败,以防出现错误。
2.1 fail-fast 机制如何实现?
- 集合中设置了一个
modCount
属性,用来记录修改次数,使用集合对象执行增,删,改中任意一个操作时,modCount
就会自动加1。 - 获取迭代器对象的时候,会给迭代器对象初始化一个
expectedModCount
属性。并且将expectedModCount
初始化为modCount
,即:int expectedModCount = modCount;
- 当使用集合对象删除元素时:
modCount
会加1。但是迭代器中的expectedModCount
不会加1。而当迭代器对象的next()
方法执行时,会检测expectedModCount
和modCount
是否相等,如果不相等,则抛出:ConcurrentModificationException
异常。 - 当使用迭代器删除元素的时候:
modCount
会加1,并且expectedModCount
也会加1。这样当迭代器对象的next()
方法执行时,检测到的expectedModCount和modCount
相等,则不会出现ConcurrentModificationException
异常。
注意:
- 虽然我们当前写的程序是单线程的程序,并没有使用多线程,但是通过迭代器去遍历的同时使用集合去删除元素,这个行为将被认定为并发修改。
- 迭代器的remove()方法删除的是next()方法的返回的那个数据。remove()方法调用之前一定是先调用了next()方法,如果不是这样的,就会报错。