在
Iterator为
HashSet类是快速失败的迭代器。从
HashSet该类的文档中:
此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间以任何方式修改集合(通过迭代器自己的remove方法除外),则迭代器将抛出ConcurrentModificationException。因此,面对并发修改,迭代器将快速而干净地失败,而不是冒着在未来不确定的时间冒任意,不确定的行为的风险。
注意,不能保证迭代器的快速失败行为,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。快速失败的迭代器会尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序的正确性是错误的:迭代器的快速失败行为应仅用于检测错误。
注意最后一句话-您正在捕获一个事实,
ConcurrentModificationException意味着另一个线程正在修改该集合。同一Javadoc
API页面还指出:
如果多个线程同时访问哈希集,并且至少有一个线程修改了哈希集,则必须在外部对其进行同步。通常,通过在自然封装了该集合的某个对象上进行同步来实现。如果不存在这样的对象,则应使用Collections.synchronizedSet方法将其“包装”
。最好在创建时完成此操作,以防止意外地异步访问集合:Set s = Collections.synchronizedSet(new HashSet(...));
我相信对Javadoc的引用在接下来应该做什么方面具有自我解释性。
此外,在您的情况下,我看不到您为什么不使用
ImmutableSet,而不是在
terms对象上创建HashSet的原因(可以在此期间进行修改;我看不到该
getTerms方法的实现,但我有一种预感基础键集正在被修改)。创建一个不可变的集合将允许当前线程拥有其自己的原始密钥集的防御性副本。
请注意,尽管
ConcurrentModificationException可以通过使用同步集(如Java
API文档中所述)来防止a,但这是所有线程都直接访问同步集合而不是后备集合的先决条件(在您的情况下,这可能是不正确的)
HashSet是在一个线程中创建的,而的基础集合
MultiMap则由其他线程修改了)。同步的收集类实际上维护了一个内部互斥体,线程可以获取该互斥体。由于您不能直接从其他线程访问互斥锁(在这里这样做是很荒谬的),因此应该使用类的方法来研究使用键集或MultiMap本身的防御性副本
unmodifiableMultimap``MultiMaps(您需要从getTerms方法返回一个不可修改的MultiMap)。您还可以研究返回同步MultiMap的必要性,但是再次,您需要确保任何线程都必须获取互斥量,以保护基础集合免受并发修改。
注意,由于我不确定是否可以确保对实际集合的并发访问,我故意省略了线程安全HashSet的使用。很有可能不是这种情况。
编辑: 在单线程方案中ConcurrentModificationException
抛出Iterator.next
这是针对以下陈述:
if(c.isSomething()) C.remove(c);在已编辑的问题中引入的。
调用
Collection.remove改变了问题的性质,因为
ConcurrentModificationException即使在单线程情况下,也有可能引发该问题。
可能是由于方法本身的使用以及
Collection迭代器的使用,在这种情况下,该变量
it是使用语句:初始化的
Iterator<BooleanClause>it = C.iterator();。
在
Iterator
it该迭代
Collection
C门店状态相关的的当前状态
Collection。在这种特殊情况下(假定为Sun /
Oracle
JRE),使用
KeyIterator(
HashMap由所使用的类的内部内部类
HashSet)来迭代
Collection。它的一个特殊特征
Iterator是它通过其方法跟踪对
Collection(
HashMap在这种情况下)进行的结构修改的次数
Iterator.remove。
当您直接调用,然后调用
remove进行
Collection后续操作时
Iterator.next,迭代器将抛出
ConcurrentModificationException,作为
Iterator.next验证是否
Collection发生了对的任何结构修改,而这些变化
Iterator是您不知道的。在这种情况下,
Collection.remove会导致结构上的修改,该修改由
Collection而不是跟踪
Iterator。
要解决问题的这一部分,您必须调用
Iterator.remove而不是
Collection.remove,因为这确保了
Iterator现在知道了对的修改
Collection。在
Iterator这种情况下,将跟踪通过发生的结构修饰
remove方法。因此,您的代码应如下所示:
final Multimap<Term, BooleanClause> terms = getTerms(bq); for (Term t : terms.keySet()) { Collection<BooleanClause> C = new HashSet(terms.get(t)); if (!C.isEmpty()) { for (Iterator<BooleanClause> it = C.iterator(); it.hasNext();) { BooleanClause c = it.next(); if(c.isSomething()) it.remove(); // <-- invoke remove on the Iterator. Removes the element returned by it.next. } } }


