的变形Collection
通过,尽管迭代Collection
使用Iterator
被不允许被大多数的Collection
类。Java库将Collection
在迭代过程中进行修改的尝试称为“并行修改”。不幸的是,这表明唯一可能的原因是多个线程同时进行了修改,但事实并非如此。仅使用一个线程就可以为Collection
(使用Collection.iterator()
或增强的for
循环)创建一个迭代器,开始进行迭代(使用Iterator.next()
或等效地进入增强的for
循环的主体),修改Collection
,然后继续进行迭代。
为了帮助程序员,这些类的某些实现尝试检测错误的并发修改,并在检测到错误时抛出。但是,通常不可能保证检测所有并发的修改。因此,错误地使用并不总是会导致抛出错误。Collection
ConcurrentModificationException
Collection
ConcurrentModificationException
的文档ConcurrentModificationException
说:
当不允许对对象进行并发修改时,检测到该对象的并发修改的方法可能会引发此异常。
请注意,此异常并不总是表示对象已被其他线程同时修改。如果单个线程发出违反对象约定的方法调用序列,则对象可能会抛出此异常...
请注意,不能保证快速故障行为,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。快速失败的操作ConcurrentModificationException
是尽力而为的。
注意
的文档HashSet
,HashMap
,TreeSet
和ArrayList
类这样说:
从该类直接或间接返回的迭代器是快速失败的:如果在创建迭代器后的任何时间修改[collection],则除了通过迭代器自己的remove方法之外,都以任何方式对其进行Iterator
抛出ConcurrentModificationException
。因此,面对并发修改,迭代器会快速干净地失败,而不会在未来的不确定时间冒着任意,不确定的行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。快速失败的迭代器会ConcurrentModificationException
尽力而为。因此,编写依赖于此异常的程序的正确性是错误的:迭代器的快速失败行为应仅用于检测错误。
再次注意,行为“无法保证”,而仅仅是“尽力而为”。
Map
接口的几种方法的文档说:
非并行实现应重写此方法,并在尽力而为的基础上,ConcurrentModificationException
如果在计算过程中检测到映射函数修改了此映射,则抛出。并发实现应重写此方法,并且在尽力而为的基础上,IllegalStateException
如果检测到映射函数在计算过程中修改了此映射,则抛出该错误,因此计算将永远不会完成。
再次注意,检测仅需要“尽力而为”,并且ConcurrentModificationException
仅针对非并发(非线程安全)类明确建议使用a 。
调试 ConcurrentModificationException
因此,当您看到由于a引起的堆栈跟踪时ConcurrentModificationException
,您不能立即假定原因是对a的不安全多线程访问Collection
。您必须检查堆栈跟踪以确定哪个类Collection
引发了异常(该类的方法将直接或间接引发该异常),以及针对哪个Collection
对象。然后,您必须检查可从何处修改该对象。
编程以防止并发修改错误
在可能的情况下,将所有引用限制在一个Collection
对象上,以便更容易防止并发修改。使Collection
一个private
物体或一个局部变量,也不要到返回引用Collection
的方法或它的迭代器。这样一来,检查可以修改的所有地方就容易得多Collection
。如果Collection
要由多个线程使用,则可以确保Collection
通过适当的同步和锁定来仅访问线程。