【发布时间】:2015-07-06 16:56:06
【问题描述】:
试图解决这个问题 2 周,但没有任何成功。 :X
当我进行任何类型的迭代时都会发生这种情况,但主要是在使用 #forEach 时。
我没有以任何方式修改列表,也没有修改元素,所以这对我来说似乎很尴尬。示例代码:
Map<Season, List<Team>> map = fetcher.getTeamsIn(ids);
Set<Team> toCreateTeams = new HashSet<>();
Set<Team> toUpdateTeams = new HashSet<>();
map.forEach((k, v) -> {
toCreateTeams.addAll(v.stream().filter(t -> !persistedTeams.containsKey(t.getId())).collect(Collectors.toSet()));
toUpdateTeams.addAll(v.stream().filter(t -> {
Date latestPersistedUpdate = persistedTeams.get(t.getId());
return latestPersistedUpdate != null && t.getLastUpdated().after(latestPersistedUpdate);
}).collect(Collectors.toSet()));
});
map 在#getTeamsIn 中被实例化为new HashMap<>();
试图在 eclipse 中打破异常,看看是否有线程在做一些疯狂的事情,但对我来说一切似乎都很正常。
在下面的图片中,在迭代 map 时抛出了异常。
我也开始出现其他一些非常奇怪的行为,比如永远卡在 lambda 表达式中。在这种情况下,在我看来 Eclipse 停止在表达式中(出于某种未知原因),就好像在该行中设置了一些断点。当我暂停执行并仅恢复有问题的线程时,流程就会恢复正常(直到下一个 lambda 表达式)或一些疯狂的 ConcurrentModificationException。
在我看来,整个事情就像是一些 Eclipse 疯狂的错误,但我真的不想重建我的环境,如果是这样的话。
我正在使用
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
在 Linux Mint 上。
谢谢!
-- 更新 1--
要明确一点:即使对于这个简单的示例,也会发生错误:
map.forEach((k, v) -> {
System.out.println("Test" + k.getId());
});
一些可能很重要的随机信息:该异常仅在打印地图的最后一个元素后才爆炸!
-- 更新 2--
关于更新 1 中的随机信息,这真的不重要,因为出于性能原因(至少在 HashMap 和 ArrayList 中),ConcurrentModificationException 仅在迭代结束时通过比较数组的实际大小来检查具有预期大小的元素。
#getTeamsIn 方法的代码:
public Map<Season, List<Team>> getTeamsIn(List<Season> seasons) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(seasons.size());
Map<Season, List<Team>> teamsInSeason = new HashMap<>();
for (Season s : seasons) {
httpclient.execute(new HttpGet(String.format(URL, s.getId())),
new Callback(latch) {
@Override
public void completed(final HttpResponse response) {
super.completed(response);
try {
teamsInSeason.put(s, new TeamsUnmarshaller().unmarshal(response.getEntity().getContent()));
}
catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
System.out.println(e);
}
}
});
}
latch.await();
return teamsInSeason;
}
Callback 类只是 implements FutureCallback<HttpResponse> 和 countDown() 以及所有回调方法(#cancelled、#completed 和 #failed)中的 latch。
【问题讨论】:
-
能否包含
map的声明? -
你能试试
map.keySet().stream().forEach()和map.entrySet().stream().forEach()看看是否有任何一组会导致同样的问题? -
是 fetcher 在后台运行获取内容,而其他内容正在打印?
-
@lhasadad,我在#getTeamsIn 中使用
CountDownLatch来等待Apache httpasyncclient 完成请求。但是 fetcher 没有在后台运行。 -
我看不出你在哪里调用 countDown。您是否有可能以某种方式多次调用倒计时,导致 getTeamsIn 方法返回早期和将来的请求完成,从而将项目添加到地图?
标签: java foreach lambda concurrentmodification