执行任何类型的迭代时,Java 8 ConcurrentModificationException

del*_*lta 6 java foreach lambda concurrentmodification

试图找出这个问题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()));
    });
Run Code Online (Sandbox Code Playgroud)

map在#getTeamsIn中实例化 new HashMap<>();

试图打破eclipse中的异常,看看是否有些线程正在做一些疯狂的事情,但一切看起来都很正常.在下面的图片中,异常是在迭代时抛出map.

ConcurrentModificationException线程堆栈跟踪 ConcurrentModificationException线程堆栈跟踪

我也开始有其他非常奇怪的行为,比如永远陷入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)
Run Code Online (Sandbox Code Playgroud)

在Linux Mint上.

谢谢!

- 更新1 -

需要明确的是:即使对于这个简单的例子,错误也在发生:

map.forEach((k, v) -> {
    System.out.println("Test" + k.getId());
});
Run Code Online (Sandbox Code Playgroud)

一些可能很重要的随机信息:例外仅在打印完地图的最后一个元素后才会爆炸!

- 更新2 -

关于Update 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;
}
Run Code Online (Sandbox Code Playgroud)

Callback类刚implements FutureCallback<HttpResponse>countDown()latch所有回调方法(#cancelled,#completed和#failed).

del*_*lta 3

好吧,刚刚发现问题了。我在 #getTeamsIn 方法中重写的 #completed 方法返回时间太长(感谢 JAXB)。由于countDown()(在 中被调用super.completed(response)) 位于 之前teamsInSeason.put(s, new TeamsUnmarshaller().unmarshal(response.getEntity().getContent()));,所以我们遇到了问题。

修复方法既简单又丑陋:

@Override
public void completed(final HttpResponse response) {
    try {
        teamsInSeason.put(s, new TeamsUnmarshaller().unmarshal(response.getEntity().getContent()));
    }
    catch (IllegalStateException | IOException e) {
        // TODO Auto-generated catch block
        System.out.println(e);
    } finally {
        super.completed(response);
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为奇怪的 Eclipse 行为(由于某种未知原因陷入某个想象的断点),如果它持续存在,那就是另一个主题的问题了。

感谢大家的帮助和时间!