java.util.ConcurrentModificationException

java.util.ConcurrentModificationException 是使用 Java 收藏类的一个非常常见的例外。Java 收藏类是错误的,这意味着如果收藏在使用 iterator 穿过它时会被更改,则 iterator.next() 会丢弃 ConcurrentModificationException

java.util.竞争对手修改例外

java.util.ConcurrentModificationException, ConcurrentModificationException, Concurrent Modification Exception, Java ConcurrentModificationException Let's see the concurrent modification exception scenario with an example.

 1package com.journaldev.ConcurrentModificationException;
 2
 3import java.util.ArrayList;
 4import java.util.HashMap;
 5import java.util.Iterator;
 6import java.util.List;
 7import java.util.Map;
 8
 9public class ConcurrentModificationExceptionExample {
10
11    public static void main(String args[]) {
12    	List<String> myList = new ArrayList<String>();
13
14    	myList.add("1");
15    	myList.add("2");
16    	myList.add("3");
17    	myList.add("4");
18    	myList.add("5");
19
20    	Iterator<String> it = myList.iterator();
21    	while (it.hasNext()) {
22    		String value = it.next();
23    		System.out.println("List Value:" + value);
24    		if (value.equals("3"))
25    			myList.remove(value);
26    	}
27
28    	Map<String, String> myMap = new HashMap<String, String>();
29    	myMap.put("1", "1");
30    	myMap.put("2", "2");
31    	myMap.put("3", "3");
32
33    	Iterator<String> it1 = myMap.keySet().iterator();
34    	while (it1.hasNext()) {
35    		String key = it1.next();
36    		System.out.println("Map Value:" + myMap.get(key));
37    		if (key.equals("2")) {
38    			myMap.put("1", "4");
39    			// myMap.put("4", "4");
40    		}
41    	}
42
43    }
44}

上面的程序将在执行时投放java.util.ConcurrentModificationException,如下所示的控制台日志。

1List Value:1
2List Value:2
3List Value:3
4Exception in thread "main" java.util.ConcurrentModificationException
5    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
6    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
7    at com.journaldev.ConcurrentModificationException.ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:22)

从输出堆栈跟踪,很明显,当我们调用 iterator next() 函数时,同时修改的例外会被扔掉。如果你想知道 iterator 是如何检查修改的,它的实现在 AbstractList 类中存在,其中定义了 int 变量 modCount。 modCount 提供了列表大小被更改的次数。 modCount 值在每个接下来的() 调用中被用来检查函数 checkForComodification() 中的任何更改。

1Map Value:3
2Map Value:2
3Map Value:4

由于我们正在更新 myMap 中的现有关键值,所以它的大小没有更改,我们没有收到 ConcurrentModificationException. 输出可能会在您的系统中有所不同,因为 HashMap键盘不像列表一样排序。

避免在多线性环境中的异常变化

  1. 您可以将列表转换为数组,然后重复在数组上。 这种方法适用于小或中等大小的列表,但如果列表大,则会影响性能很多
  2. 您可以在重复过程中将列表锁定,将其放入同步块中。 这种方法不建议,因为它将停止使用多线 3。 如果您使用 JDK1.5 或更高,则可以使用 ConcurrentHashMapCopyOnWriteArrayList类。

避免在单线环境中的异常变化

您可以使用 iterator remove() 函数将对象从基础集合对象中删除,但在这种情况下,您可以从列表中删除相同的对象,而不是任何其他对象。

 1package com.journaldev.ConcurrentModificationException;
 2
 3import java.util.Iterator;
 4import java.util.List;
 5import java.util.Map;
 6import java.util.concurrent.ConcurrentHashMap;
 7import java.util.concurrent.CopyOnWriteArrayList;
 8
 9public class AvoidConcurrentModificationException {
10
11    public static void main(String[] args) {
12
13    	List<String> myList = new CopyOnWriteArrayList<String>();
14
15    	myList.add("1");
16    	myList.add("2");
17    	myList.add("3");
18    	myList.add("4");
19    	myList.add("5");
20
21    	Iterator<String> it = myList.iterator();
22    	while (it.hasNext()) {
23    		String value = it.next();
24    		System.out.println("List Value:" + value);
25    		if (value.equals("3")) {
26    			myList.remove("4");
27    			myList.add("6");
28    			myList.add("7");
29    		}
30    	}
31    	System.out.println("List Size:" + myList.size());
32
33    	Map<String, String> myMap = new ConcurrentHashMap<String, String>();
34    	myMap.put("1", "1");
35    	myMap.put("2", "2");
36    	myMap.put("3", "3");
37
38    	Iterator<String> it1 = myMap.keySet().iterator();
39    	while (it1.hasNext()) {
40    		String key = it1.next();
41    		System.out.println("Map Value:" + myMap.get(key));
42    		if (key.equals("1")) {
43    			myMap.remove("3");
44    			myMap.put("4", "4");
45    			myMap.put("5", "5");
46    		}
47    	}
48
49    	System.out.println("Map Size:" + myMap.size());
50    }
51
52}

上面的程序的输出显示在下方. 你可以看到没有ConcurrentModificationException被程序扔掉。

 1List Value:1
 2List Value:2
 3List Value:3
 4List Value:4
 5List Value:5
 6List Size:6
 7Map Value:1
 8Map Value:2
 9Map Value:4
10Map Value:5
11Map Size:4

从上面的例子看,很明显:

  1. 联合国 同时期收藏类可以安全地被修改,它们不会抛出同时期的ModizationException.
  2. 对于CopyOnWriteArrayList,执行者不适应列表中的变动,在原始列表上工作.
  3. 对于同源HashMap,行为并不总是一样. 条件 : 如果 (key.equals ("1") { myMap.remove ("3")} 输出是 :_ 地图值 : 地图值 : (- ) 地图值 : (- ) 地图值 : (- ) 地图值 : (- ) 地图值 : (- ) 它取用新添加了"4"键的新对象,而不是下一个添加了"5"键的新对象. 现在,如果我将条件更改为下面. { if(key.equers ("3"){ myMap.remove ("2")}} {输出为:_ 地图值: 地图值: 地图值: 地图值: 地图值: 在这种情况下,它不考虑新添加的对象. 因此,如果您正在使用 ConformatHashMap, 那么将避免添加新的对象, 因为它可以根据密钥集进行处理 。 注意同一程序可以在您的系统中打印出不同的值, 因为 HashMap 密钥集没有命令 。 (单位:千美元) (英语)

使用循环以避免 java.util.ConcurrentModification例外

如果您正在使用单线程环境,并希望您的代码来处理列表中的额外添加对象,那么您可以使用循环而不是 Iterator来做到这一点。

1for(int i = 0; i<myList.size(); i++){
2    System.out.println(myList.get(i));
3    if(myList.get(i).equals("3")){
4    	myList.remove(i);
5    	i--;
6    	myList.add("6");
7    }
8}

请注意,我正在减少计数器,因为我正在删除相同的对象,如果你必须删除下一个或更远的对象,那么你不需要减少计数器。

 1package com.journaldev.ConcurrentModificationException;
 2
 3import java.util.ArrayList;
 4import java.util.List;
 5
 6public class ConcurrentModificationExceptionWithArrayListSubList {
 7
 8    public static void main(String[] args) {
 9
10    	List<String> names = new ArrayList<>();
11    	names.add("Java");
12    	names.add("PHP");
13    	names.add("SQL");
14    	names.add("Angular 2");
15
16    	List<String> first2Names = names.subList(0, 2);
17
18    	System.out.println(names + " , " + first2Names);
19
20    	names.set(1, "JavaScript");
21    	// check the output below. :)
22    	System.out.println(names + " , " + first2Names);
23
24    	// Let's modify the list size and get ConcurrentModificationException
25    	names.add("NodeJS");
26    	System.out.println(names + " , " + first2Names); // this line throws exception
27
28    }
29
30}

上述方案的结果是:

 1[Java, PHP, SQL, Angular 2] , [Java, PHP]
 2[Java, JavaScript, SQL, Angular 2] , [Java, JavaScript]
 3Exception in thread "main" java.util.ConcurrentModificationException
 4    at java.base/java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1282)
 5    at java.base/java.util.ArrayList$SubList.listIterator(ArrayList.java:1151)
 6    at java.base/java.util.AbstractList.listIterator(AbstractList.java:311)
 7    at java.base/java.util.ArrayList$SubList.iterator(ArrayList.java:1147)
 8    at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:465)
 9    at java.base/java.lang.String.valueOf(String.java:2801)
10    at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)
11    at com.journaldev.ConcurrentModificationException.ConcurrentModificationExceptionWithArrayListSubList.main(ConcurrentModificationExceptionWithArrayListSubList.java:26)

根据 ArrayList subList 文档,仅允许通过 subList 方法返回的列表进行结构更改。

您可以从我们的 GitHub 存储库下载所有示例代码。

Published At
Categories with 技术
Tagged with
comments powered by Disqus