private Set<Job> myJobs = new HashSet<>();
public shouldDoWork(Work work)
return !myJobs.stream()
.map(job -> job.doWork(work))
.anyMatch(shouldDoWork -> !shouldDoWork);
public addJob(Job job) {
myJobs.add(job);
}
// also for remove
并且有许多线程随时调用此函数中的任何一个
java.util.ConcurrentModificationException 在java.util.HashMap $ KeySpliterator.tryAdvance(HashMap.java:1579)〜[?:1.8.0_202] 在java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)〜[?:1.8.0_202] 在java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)〜[?:1.8.0_202] 在java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)〜[?:1.8.0_202] 在java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)〜[?:1.8.0_202] 在java.util.stream.MatchOps $ MatchOp.evaluateSequential(MatchOps.java:230)〜[?:1.8.0_202] 在java.util.stream.MatchOps $ MatchOp.evaluateSequential(MatchOps.java:196)〜[?:1.8.0_202] 在java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)〜[?:1.8.0_202] 在java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:449)〜[?:1.8.0_202]
知道为什么会引发ConcurrentModificationException吗?
是因为集合(myJobs
)发生了变化,还是因为shouldDoWork
的值被其他变化了?
答案 0 :(得分:1)
知道为什么会引发ConcurrentModificationException吗?
是的
是因为集合(myJobs)正在更改,还是因为 应该由其他方式更改工作吗?
前者。您列出了两种方法,shouldDoWork()
和addJob()
。第一个对集合myJobs
执行流操作,第二个向该集合添加元素。如果确实如此
有很多线程随时调用此函数
那么很可能某个线程将在某个时刻调用addJob()
,从而在另一个人从该集合构造流的时间之间,对结构myJobs
进行结构性修改。当其他人消耗完该流时。修改集合中的对象不会导致ConcurrentModificationException
(尽管这样做通常会使集合无效,如果这样做会更改元素的哈希码)。它正在修改可以做的集合本身。
实际上,您很幸运获得了CME,因为在您所描述的同步修改不正确的情况下,这不能保证。相反,您可能会得到垃圾结果。
对于两者,您至少都有两个合理的选择,可以实现正确的同步并避免CME:
同步对myJobs
的访问。例如,
public boolean shouldDoWork(Work work) {
synchronized(myJobs) {
return !myJobs.stream()
.map(job -> job.doWork(work))
.anyMatch(shouldDoWork -> !shouldDoWork);
}
}
public addJob(Job job) {
synchronized (myJobs) {
myJobs.add(job);
}
}
如果采用这种方法,则必须类似地将所有访问同步到myJobs
。或
使用不需要显式同步的另一种容器(例如ConcurrentHashMap
)
private ConcurrentHashMap<Job, Job> myJobs = new ConcurrentHashMap<>();
public boolean shouldDoWork(Work work) {
return !myJobs.keySet().stream()
.map(job -> job.doWork(work))
.anyMatch(shouldDoWork -> !shouldDoWork);
}
public addJob(Job job) {
myJobs.put(job, job);
}
在实施此类更改之前,您应该阅读提议的替代类(在这种情况下为ConcurrentHashMap
)的文档,以确保您理解其中的含义。此替代方法可能比同步性能更好,但这是以较弱的行为保证为代价的。