Search code examples
javaconcurrentmodification

ConcurrentModificationException and multiple catch blocks


When I ran the following code

Caught expected ConcurrentModificationException

is printed which is expected but strange thing is on second iteration no exception is being caught and

Failed to catch expected ConcurrentModificationException

is printed. I am really not sure why for second time it is not caught.

public class TestLHS {

    public static void main(String[] args) {
        LinkedHashSet<Integer> lhSet = new LinkedHashSet<Integer>();
        Integer one = new Integer(1);
        Integer two = new Integer(2);
        Integer three = new Integer(3);
        Integer four = new Integer(4);
        Integer cinco = new Integer(5);

        // Add the three objects to the LinkedHashSet.
        // By its nature, the LinkedHashSet will iterate in
        // order of insertion.
        lhSet.add(one);
        lhSet.add(two);
        lhSet.add(three);

        // 1. Iterate over set. try to insert while processing the
        // second item. This should throw a ConcurrentModificationEx
        try {
            for (Iterator<Integer> it = lhSet.iterator(); it.hasNext();) {
                Integer num = (Integer) it.next();
                if (num == two) {
                    lhSet.add(four);
                }
                System.out.println(num);
            }
        } catch (ConcurrentModificationException ex) {
            System.out.println("Caught expected ConcurrentModificationException");
        }

        // 2. Iterate again, this time inserting on the (old) 'last'
        // element. This too should throw a ConcurrentModificationEx.
        // But it doesn't.
        try {
            for (Iterator<Integer> it = lhSet.iterator(); it.hasNext();) {
                Integer num = (Integer) it.next();
                if (num == four) {
                    lhSet.add(cinco);
                }
                System.out.println(num);
            }

            System.out.println("Failed to catch expected ConcurrentModificationException");
        } catch (ConcurrentModificationException ex) {
            System.out.println("Caught expected ConcurrentModificationException");
        }

    }
}

Can someone explain this behavior?


Solution

  • Let's look at the documentation:

    if the set is modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.

    Notice that it's not clear exactly which iterator method will throw the exception. Let's check the source:

    public final boolean hasNext() {
        return next != null;
    }
    
    // called by LinkedKeyIterator.next()
    final LinkedHashMap.Entry<K,V> nextNode() {
        LinkedHashMap.Entry<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        current = e;
        next = e.after;
        return e;
    }
    

    As you can see, the exception is thrown by the next() method, not by hasNext(). Since four is the last element in the set, next is already null, so the next call to hasNext() returns false and next() isn't called again. Therefore, the concurrent modification is not observed and no exception is thrown.

    See also Why isn't this code causing a ConcurrentModificationException?