異常:java.util.ConcurrentModificationException
阿新 • • 發佈:2018-11-11
簡單的小程式碼貼上:
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集合。
如有不對之處或者你們有更好的解決方式,希望能夠告知。感謝!