Search code examples
javaforeachlambdaconcurrentmodification

Java 8 ConcurrentModificationException when doing any kind of iteration


Trying to figure out this problem for 2 weeks, but without any success. :X

It's occurring when I'm doing any kind of iteration, but mostly when using #forEach.

I'm not modifying the list, nor it's elements, in any way, so this seems very awkward to me. Example code:

    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 is instantiated in #getTeamsIn with new HashMap<>();

Tried to break on exception in eclipse to see if some thread was doing some crazy shit, but everything seems pretty normal to me. In the pics below, the exception was throw while iterating the map.

ConcurrentModificationException thread stack trace ConcurrentModificationException thread stack trace

I started having some other very strange behaviours too, like getting stuck in a lambda expression forever. In this case, it seems to me that Eclipse is stopping in the expression (for some unknown reason), as if some breakpoint was set in the line. When I suspend the execution and resume just the problematic Thread the flow just get backs to normal (until the next lambda expression) or some crazy ConcurrentModificationException.

stopping in expression for unknown reason

The whole thing seems like some Eclipse crazy bug to me, but I really don't want to rebuild my environment, if that's the case.

I'm using

Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

on Linux Mint.

Thanks!

-- Update 1 --

Just to be clear: The error is occurring even for this simple example:

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

Some random info that might be important: The exception only exploded after printing the last element of the map!

-- Update 2 --

Regarding the random info in Update 1, that's really unimportant, since, for performance reasons (at least in HashMap and ArrayList), ConcurrentModificationException is only checked in the end of iterations, by comparing the actual size of the array of elements with the expected size of it.

The code of the method #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 class just implements FutureCallback<HttpResponse> and countDown() the latch in all callback methods (#cancelled, #completed and #failed).


Solution

  • Ok, just found out the problem. My overrided #completed method in the method #getTeamsIn is taking too long (thanks to JAXB) to return. Since the countDown() (being called in super.completed(response)) is before the teamsInSeason.put(s, new TeamsUnmarshaller().unmarshal(response.getEntity().getContent()));, we have a problem.

    The fix is simple and ugly:

    @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);
        }
    }
    

    The weird Eclipse behaviour (getting stuck in some imaginary breakpoint for some unknown reason), if it persists, is a problem for another topic, I think.

    Thank you all for the help and time!