ConcurrentModificationException和多个catch块

时间:2017-06-15 05:47:56

标签: java concurrentmodification

当我运行以下代码时

  

捕获预期的ConcurrentModificationException

打印

是预期的,但奇怪的是第二次迭代没有异常被捕获

  

无法捕获预期的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");
        }

    }
}

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

4 个答案:

答案 0 :(得分:1)

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

你在第一个for循环中得到ConcurrentModificationException,因为你添加了&#39; num&#39; ==&#39;两个&#39 ;;迭代器仍然有三个&#39;离开,所以循环继续。随后对next()的调用是抛出异常的地方。

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

答案 1 :(得分:1)

让我们看一下tutorial

  

如果在创建迭代器之后的任何时候修改了set,​​除了通过迭代器自己的remove方法之外,迭代器将抛出ConcurrentModificationException

请注意,不清楚哪个迭代器方法会抛出异常。我们来查看documentation

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()的下一次调用将返回false,并且不会再次调用next()。因此,没有观察到并发修改,也没有抛出异常。

另见source

答案 2 :(得分:1)

这是一个已知的问题,编号 6258302 ,发布在JDK bugList。同样可以找到here

答案 3 :(得分:0)

Java Docs:

中明确提到了这一点

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

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

: - 编写一个依赖于此异常的程序以确保其正确性是错误的:ConcurrentModificationException仅用于检测错误。

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