简单的小代码贴上:
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方式(读|读都有锁), 尽量用其他集合替代.
解决方式:
- Collection users = new CopyOnWriteArrayList();
- List集合有个方法:list.listIterator(); 返回的是一个ListIterator,它是Iterator的子接口,通过这个对象可以实现对List集合的迭代,同时可对集合进行元素的增删改。只适用于List集合。只适用于List集合。只适用于List集合。
- 用迭代器来操作当前的元素,而不是用集合对象来操作
至于出现上面输出的原因:可以查看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,抛出异常!。
如有不对之处或者你们有更好的解决方式,希望能够告知。感谢!