ConcurrentModificationException 和多个 catch 块

ConcurrentModificationException and multiple catch blocks

当我运行下面的代码

Caught expected ConcurrentModificationException

被打印出来,这是预期的,但是 st运行ge 是在第二次迭代中没有异常被捕获并且

Failed to catch expected ConcurrentModificationException

已打印。真不知道为什么第二次没抓到。

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

    }
}

有人可以解释这种行为吗?

Java文档中明确提到了这一点:-

那个- 此异常并不总是表示对象已被不同的线程并发修改。如果单个线程发出一系列违反对象契约的方法调用,则该对象可能会抛出此异常。例如,如果线程在使用快速失败迭代器迭代集合时直接修改集合,则迭代器将抛出此异常

注意:-无法保证快速失败行为,一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。快速失败操作会尽最大努力抛出 ConcurrentModificationException。 因此,

:- 编写依赖于此异常的正确性的程序是错误的:ConcurrentModificationException 应该仅用于检测错误。

更多详情参考- https://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html

如果您查看 LinkedHashMap(或任何实现 Iterator 的 class)的代码,您将看到在 next() 方法中检查了 ConcurrentModificationException。

您在第一个 for 循环中得到 ConcurrentModificationException,因为您添加了 when 'num' == 'two';迭代器仍然有 'three' 剩余,因此循环继续。随后调用 next() 是抛出异常的地方。

在第二个循环中,您在迭代最后一个当前元素时添加,因此您不会调用 next() 并且不会抛出 ConcurrentModificationException。

我们来看看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.

请注意,尚不清楚 哪个 迭代器方法会抛出异常。让我们检查一下 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;
}

如您所见,异常是由 next() 方法抛出的,而不是 hasNext() 抛出的。由于 four 是集合中的最后一个元素,next 已经为空,因此下一次调用 hasNext() returns false 并且 next() 不会再次调用.因此,没有观察到并发修改,也没有抛出异常。

另见 Why isn't this code causing a ConcurrentModificationException?

这是一个已知问题,编号为 6258302,已发布在 JDK 错误列表中。同样可以查到here