Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 traceConcurrentModificationException 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).

like image 396
delta Avatar asked Apr 28 '15 02:04

delta


People also ask

How can you avoid ConcurrentModificationException during the iteration of a collection?

To Avoid ConcurrentModificationException in single-threaded environment. You can use the iterator remove() function to remove the object from underlying collection object. But in this case, you can remove the same object and not any other object from the list.

How do I fix ConcurrentModificationException in Java?

How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.

Does iterator throw a ConcurrentModificationException?

ConcurrentModificationException is not thrown by Iterator. remove() because that is the permitted way to modify an collection while iterating.


1 Answers

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!

like image 197
delta Avatar answered Sep 22 '22 01:09

delta