这个寒假看的《Java并发编程实战》终于派上了用场,今天搬砖过程中遇到了ConcurrentModificationException
,结果还是不会。之后晚上回去恶补一波,写此文章。
JCP -> 《Java并发编程实战》
Java并发-目录
Single Thread
JCP 第五章:在单线程代码中也可能抛出ConcurrentModificationException
。当对象直接从容器中删除而不是通过Iterator.remove
来删除时,就会抛出这个异常。
虽然ConcurrentModificationException
这个异常听上去是多线程环境下出现的。但是如上面的引用,单线程里如果使用不当也会出现。
1
2
3
4
5
6
7
8
|
private final ArrayList<Integer> list = new ArrayList<>();
private void iterateAndRemove() {
for (Integer i : list) {
if (i % 4 == 0) {
list.remove(i);
}
}
}
|
在使用for-each
遍历列表时,实际上就是用Iterator
遍历,但在删除的时候使用了list.remove()
,这就造成了ArrayList
中的modCount
和Iterator
中的modCount
不相同,从而抛出异常。
解决办法也很简单,只要使用Iterator.remove
就不会有异常。所以即使在单线程中也不能混用Iterator
和容器中的方法。
Multi Thread
多线程的情况下,情况变得更加复杂。我们来看下面的实例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class ConcurrentModificationExceptionTest {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
private final ArrayList<Integer> list = new ArrayList<>();
// works
private final Runnable iteratorRunnable = () -> {
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
int i = it.next();
if (i % 4 == 0) {
it.remove();
System.out.println("Removed " + i);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
public ConcurrentModificationExceptionTest() {
for (int i = 0; i < 100; i++) {
list.add(i);
}
}
public void testArrayList() {
for (int i = 0; i < 20; i++) {
executor.execute(iteratorRunnable);
executor.execute(() -> {
for (Integer integer : list) {
System.out.println(integer);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
public void stop() throws InterruptedException {
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
public static void main(String[] args) throws InterruptedException {
ConcurrentModificationExceptionTest test = new ConcurrentModificationExceptionTest();
test.testArrayList();
test.stop();
}
}
|
上面的代码也会造成ConcurrentModificationException
,因为多个线程同时遍历列表,也可能造成ArrayList
中的modCount
和Iterator
中的modCount
不相同。
Solution
1
2
3
4
5
|
synchronized (list) {
for (Integer integer : list) {
//doSomething
}
}
|
1
2
3
4
|
ArrayList<Integer> cloneList = (ArrayList<Integer>) list.clone();
for (Integer integer : cloneList) {
//doSomething
}
|
参考