異常拋出原因
在使用remove方法對ArrayList進(jìn)行刪除操作時剃法,會拋出此異常。
代碼分析
測試用戶類:
package sort;
public class User implements Comparable<User>{
private int id;
private String name;
private int birthDay;
public User(int id, String name, int birthDay) {
this.id = id;
this.name = name;
this.birthDay = birthDay;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getBirthDay() {
return birthDay;
}
@Override
public int compareTo(User user) {
return user.getId() - this.getId();
}
}
各種remove方法測試:
package sort;
import com.google.common.collect.Lists;
import java.util.Iterator;
import java.util.List;
public class SortTest1 {
public static void main(String[] args) {
foreachRemove();
iteratorRemove();
iteratorHasNextCheck();
}
private static void iteratorHasNextCheck() {
List<User> list = initUserList();
Iterator<User> iterator = list.iterator();
while(iterator.hasNext()){
User user = iterator.next();
if("四".equals(user.getName())){
list.remove(user);
}else{
System.out.println(user.getName());
}
}
}
private static void iteratorRemove() {
try {
List<User> list = initUserList();
Iterator<User> iterator = list.iterator();
while (iterator.hasNext()) {
User user = iterator.next();
if (user.getBirthDay() < 20040101) {
iterator.remove();
}
}
System.out.println(list.size());
} catch (Exception e) {
e.printStackTrace();
System.out.println("iteratorRemove failed");
}
}
private static void foreachRemove() {
try {
List<User> list = initUserList();
for (User user : list) {
if (user.getBirthDay() < 20040101) {
list.remove(user);
}
}
System.out.println(list.size());
} catch (Exception e) {
e.printStackTrace();
System.out.println("foreachRemove failed");
}
}
private static List<User> initUserList() {
List<User> list = Lists.newArrayList();
list.add(new User(1,"一", 20010101));
list.add(new User(2,"二", 20020202));
list.add(new User(3,"三", 20030303));
list.add(new User(4,"四", 20040404));
list.add(new User(5,"五", 20050505));
return list;
}
}
結(jié)果:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at sort.SortTest1.foreachRemove(SortTest1.java:49)
at sort.SortTest1.main(SortTest1.java:11)
foreachRemove failed
2
一
二
三
在分析結(jié)果前律适,先貼出ArrayList的迭代器源碼
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
分析:
1.可以看到hasNext()方法主要是比對當(dāng)前元素的數(shù)組下標(biāo)與迭代器元素的個數(shù)如捅。
2.next方法要先檢查ArrayList的操作數(shù)(modCount虎囚,ArrayList父類AbstractList的成員變量,
這個成員變量記錄著集合的修改次數(shù)五慈,也就每次add或者remove它的值都會加1)有沒有改變纳寂,如果改變就會拋出ConcurrentModificationException異常。
- foreachRemove里使用了ArrayList的remove方法泻拦,modCount發(fā)生了變化毙芜,所以拋出異常。
- iteratorRemove里使用了ArrayList內(nèi)部類Itr的remove方法争拐,modCount未發(fā)生變化腋粥,所以正常執(zhí)行。
- iteratorHasNextCheck里正好移除了第4個元素架曹,此時雖然modCount變化了隘冲,但是元素總數(shù)與索引下標(biāo)都是4,此時hasNext()返回false绑雄,所以不會往下執(zhí)行展辞。
- iteratorHasNextCheck里如果移除前3個元素,依舊會拋出ConcurrentModificationException異常绳慎。
線上問題
線上使用guava本地緩存纵竖,并且每次拿出數(shù)據(jù)會再次進(jìn)行Collections.sort()排序,導(dǎo)致拋出此異常杏愤。
偽代碼
:
package sort;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SortTest2 {
private static Map<String, List<User>> map = Maps.newHashMap();
public static void main(String[] args) {
buildMap();
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
for (int i=0; i<5; i++) {
executor.execute(() -> {
try {
Collections.sort(map.get("list"));
Thread.sleep(1000L);
} catch (Exception e) {
e.printStackTrace();
System.out.println("sort thread error");
}
});
}
executor.shutdown();
}
private static void buildMap() {
map.put("list", initUserList());
}
private static List<User> initUserList() {
List<User> list = Lists.newArrayList();
list.add(new User(3,"三", 20030303));
list.add(new User(2,"二", 20020202));
list.add(new User(4,"四", 20040404));
list.add(new User(1,"一", 20010101));
list.add(new User(5,"五", 20050505));
return list;
}
}
結(jié)果(報錯數(shù)量不定靡砌,取決于并發(fā)數(shù)與排序執(zhí)行時間):
java.util.ConcurrentModificationException
at java.util.ArrayList.sort(ArrayList.java:1464)
at java.util.Collections.sort(Collections.java:141)
at sort.SortTest2.lambda$main$0(SortTest2.java:24)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
sort thread error
java.util.ConcurrentModificationException
at java.util.ArrayList.sort(ArrayList.java:1464)
at java.util.Collections.sort(Collections.java:141)
at sort.SortTest2.lambda$main$0(SortTest2.java:24)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
sort thread error
java.util.ConcurrentModificationException
at java.util.ArrayList.sort(ArrayList.java:1464)
at java.util.Collections.sort(Collections.java:141)
at sort.SortTest2.lambda$main$0(SortTest2.java:24)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
sort thread error
ArrayList中的sort()方法
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
主要調(diào)用了Arrays的sort()方法,此排序方法本文暫不介紹,后續(xù)modCount進(jìn)行了自增珊楼,此處在多個線程一起執(zhí)行下會出現(xiàn)問題通殃,拋出ConcurrentModificationException異常。