1. 程式人生 > >異常:java.util.ConcurrentModificationException

異常:java.util.ConcurrentModificationException

簡單的小程式碼貼上:

public class ConcurrentModificationExceptionExample {
	public static void main(String[] args) {
		Collection<User> users = new ArrayList<>();
		users.add(new User("張三", 28));
		users.add(new User("李四", 25));
		users.add(new User("王五", 31));
		Iterator itrUsers = users.iterator();
		
while (itrUsers.hasNext()) { User user = (User) itrUsers.next(); if ("李四".equals(user.getName())) { users.remove(user);                          } else { System.out.println(user);
} } } } class User{ private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) {
this.age = age; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } }

最後的執行結果有點詭異:

當要remove集合中的元素是name為'張三'的時候:
	Exception in thread "main" java.util.ConcurrentModificationException
		at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
		at java.util.ArrayList$Itr.next(Unknown Source)
		at studythread.ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:23)
當要remove集合中的元素是name為'李四'的時候:
	User [name=張三, age=28]
當要remove集合中的元素是name為'王五'的時候:
	User [name=張三, age=28]
	User [name=李四, age=25]
	Exception in thread "main" java.util.ConcurrentModificationException
		at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
		at java.util.ArrayList$Itr.next(Unknown Source)
		at studythread.ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:34)

總是在碰到這個問題才想起!委屈

記得還是看張孝祥老師的java多執行緒與併發庫視訊教程才大概明白原因!

!!!是否還記得在學習List集合的時候,有說過,

在迭代的過程中不要使用集合操作容易出現異常

JDK5之後提出了很多執行緒安全的容器, 與前輩synchronized方式比起來, 它們的亮點並不是保證了執行緒安全, 而是它們在保證執行緒安全的同時儘量避免併發瓶頸
基本上限制條件多的容器都能實現Concurrent版本, 保持一定的讀寫併發;  像ArrayList LinkedList很難避開併發瓶頸, 退而求其次ArrayList實現了CopyOn保證了讀併發;
LinkedList只能是通過Collections.synchronizedList()的synchronized方式(讀|讀都有鎖), 儘量用其他集合替代.
解決方式1:Collection users = new CopyOnWriteArrayList();

至於原因:可以檢視AbstractList這個類的原始碼,
在你呼叫迭代器的next()方法時,它的實現類中會呼叫checkForComodification()方法,
if (modCount != expectedModCount)
就是判斷修改的次數和預期修改次數是否一致,若不一致就會丟擲異常。
就是說在這個集合中,你對這個集合進行刪除,新增,在迭代器的實現類中有個屬性來記錄你操作的次數。

當獲取迭代器的時候modCount應該是3(進行了三次add),
那麼當你獲取迭代器,其實=實現類內部一例項化的時候就會將modCount賦值給expectedModCount,
再remove()的時候modCount就是4了,remove完了之後再去next(),一檢查,
不一致,那麼就報錯嘍。

當要移除的是李四的時候,呼叫hasNext()方法,
返回的就是:cursor != size();的值
其中cursor變數,記錄的是操作下元素的角標,size()就是這個集合的大小,
當你進行remove的時候這個集合的大小已經是2了,在上一次next()的時候cursor已經是2了,
so?2!=2,返回的肯定是false,while迴圈結束,就沒有進到這個next()方法中去,就沒丟擲異常。

當要移除的是王五時,cursor的值已經是3了,remove()方法之後size()的值是2,呼叫hashNext()的時候,返回true,
當獲取迭代器的時候modCount是3(進行了三次add),內部將expectedModCount=modCount,
remove()方法執行之後modCount就是為4了,執行到next()的時候,內部呼叫checkForComodification,
if (modCount != expectedModCount)throw new ConcurrentModificationException();
3!=4,丟擲異常!。

方式2:List集合有個方法:list.listIterator();
返回的是一個ListIterator,它是Iterator的子介面,
通過這個物件可以實現對List集合的迭代,同時可對集合進行元素的增刪改。

只適用於List集合只適用於List集合只適用於List集合。


如有不對之處或者你們有更好的解決方式,希望能夠告知。感謝!