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
当我运行下面的代码
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 aConcurrentModificationException
.
请注意,尚不清楚 哪个 迭代器方法会抛出异常。让我们检查一下 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