在多線程的情況下是由Iterator遍歷修改集合對象,報ConcurrentModificationException()異常的根因分析。
在多線程下使用Iterator來迭代對象時,總會包ConcurrentModificationException();異常,經過我對list和Iterator相關源碼的分析,終于搞明白了這個問題:
下面結合源代碼來討論這個問題:
1、當我們調用一個List的iterator時,其實返回的并不是Itr對象(Iterator是一個接口),請看代碼:
2、在Itr類中有這么一句話 int expectedModCount = modCount;首先講一下這兩個變量的含義:
expectedModCount :創建當前的Itr對象時集合對象被修改的次數。他是Itr的變量。
modCount:記錄集合對象從創建到當前時間被做修改的次數。(集合每進行一次增刪改查都會使modCount),他是Itr的外部類AbstractList
的變量,且該變量在 AbstractList中被如此修飾protected transient int modCount = 0;
另外,Itr自己對集合對象進行了修改后,他會維持expectedModCount 和modCount的保持相等,請看以下代碼
在代碼的12行,Itr保證了expectedModCount 和modCount值的相等 ,modCount的值發生了改變嗎,他改變了,帶代碼的第8行改變的,請看此處的代碼:
在代碼的第四行modCount發生了改變。 由此可以看出,在我們調用集合對象的iterator()方法的remove時總會使list的modCount的值自增1,但是Itr會自己維護該值和expectedModCount 的一致。
3、試問:如果expectedModCount 和modCount的值如果不相等,會有什么問題呢,這就是報ConcurrentModificationException();異常的原因所在,請先看Itr的next()方法和next()調用的方法
經過以上的分析,發現拋出ConcurrentModificationException異常處于調用next()方法時,比較expectedModCount 和modCount的值,如果兩個值不相等,就會拋出異常,然而在什么情況下會使expectedModCount 和modCount的值不相等呢,只有在兩個Itr同時對一個list進行操作的時候才會出現這樣的問題,所以在以后的編碼過程中在是由Iterator進行remove()時一定要考慮是否時多線程的,如果是請不要用Iterator進行remove(),而應該使用List的remove方法進行。
下面結合源代碼來討論這個問題:
1、當我們調用一個List的iterator時,其實返回的并不是Itr對象(Iterator是一個接口),請看代碼:
1
public Iterator<E> iterator() {
2
return new Itr();
3
}

2

3

2、在Itr類中有這么一句話 int expectedModCount = modCount;首先講一下這兩個變量的含義:
expectedModCount :創建當前的Itr對象時集合對象被修改的次數。他是Itr的變量。
modCount:記錄集合對象從創建到當前時間被做修改的次數。(集合每進行一次增刪改查都會使modCount),他是Itr的外部類AbstractList
的變量,且該變量在 AbstractList中被如此修飾protected transient int modCount = 0;
另外,Itr自己對集合對象進行了修改后,他會維持expectedModCount 和modCount的保持相等,請看以下代碼
1 //Itr的刪除方法
2 public void remove() {
3 if (lastRet == -1)
4 throw new IllegalStateException();
5 checkForComodification();
6
7 try {
8 AbstractList.this.remove(lastRet);
9 if (lastRet < cursor)
10 cursor--;
11 lastRet = -1;
12 expectedModCount = modCount;
13 } catch(IndexOutOfBoundsException e) {
14 throw new ConcurrentModificationException();
15 }
16 }
2 public void remove() {
3 if (lastRet == -1)
4 throw new IllegalStateException();
5 checkForComodification();
6
7 try {
8 AbstractList.this.remove(lastRet);
9 if (lastRet < cursor)
10 cursor--;
11 lastRet = -1;
12 expectedModCount = modCount;
13 } catch(IndexOutOfBoundsException e) {
14 throw new ConcurrentModificationException();
15 }
16 }
在代碼的12行,Itr保證了expectedModCount 和modCount值的相等 ,modCount的值發生了改變嗎,他改變了,帶代碼的第8行改變的,請看此處的代碼:
1
public E remove(int index) {
2
RangeCheck(index);
3
4
modCount++;
5
E oldValue = elementData[index];
6
7
int numMoved = size - index - 1;
8
if (numMoved > 0)
9
System.arraycopy(elementData, index+1, elementData, index,
10
numMoved);
11
elementData[--size] = null; // Let gc do its work
12
13
return oldValue;
14
}

2

3

4

5

6

7

8

9

10

11

12

13

14

在代碼的第四行modCount發生了改變。 由此可以看出,在我們調用集合對象的iterator()方法的remove時總會使list的modCount的值自增1,但是Itr會自己維護該值和expectedModCount 的一致。
3、試問:如果expectedModCount 和modCount的值如果不相等,會有什么問題呢,這就是報ConcurrentModificationException();異常的原因所在,請先看Itr的next()方法和next()調用的方法
1
//Itr的next方法:
2
public E next() {
3
checkForComodification();
4
try {
5
E next = get(cursor);
6
lastRet = cursor++;
7
return next;
8
} catch(IndexOutOfBoundsException e) {
9
checkForComodification();
10
throw new NoSuchElementException();
11
}
12
}
13
14
//checkForComodification()方法:
15
final void checkForComodification() {
16
if (modCount != expectedModCount)
17
throw new ConcurrentModificationException();
18
}
19
}
20
在next方法的一開始顯示調用了checkForComodification()方法(見第三行),在checkForComodification()方法中做的工作就是比較expectedModCount 和modCount的值是否相等,如果不相等,就認為還有其他對象正在對當前的List進行操作,那個就會拋出ConcurrentModificationException異常。
2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

經過以上的分析,發現拋出ConcurrentModificationException異常處于調用next()方法時,比較expectedModCount 和modCount的值,如果兩個值不相等,就會拋出異常,然而在什么情況下會使expectedModCount 和modCount的值不相等呢,只有在兩個Itr同時對一個list進行操作的時候才會出現這樣的問題,所以在以后的編碼過程中在是由Iterator進行remove()時一定要考慮是否時多線程的,如果是請不要用Iterator進行remove(),而應該使用List的remove方法進行。