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