栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么it.next()抛出java.util.ConcurrentModificationException?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么it.next()抛出java.util.ConcurrentModificationException?

在

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.     } }        }


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/438527.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号