1. 程式人生 > >ConcurrentModificationException(並發修改異常)的分析

ConcurrentModificationException(並發修改異常)的分析

equals div 編譯 nbsp expec 沒有 soft 常見錯誤 col

1)介紹
並發修改ConcurrentModificationException錯誤是開發中一個常見錯誤,多發生在對一個Collection邊遍歷邊做影響size變化的操作中,下面以ArrayList為例分析ConcurrentModificationException錯誤。

2)分析
ArrayList初始數據如下:

1         List<Integer> list = new ArrayList<Integer>();
2         list.add(1);
3         list.add(2);
4         list.add(3);

【場景1】不會有並發修改錯誤

1         int length = list.size();
2         for (int i = 0; i < length; i++) {
3             if (list.get(i).equals(2)) {
4                 list.add(10);
5             }
6         }

【場景2】會有並發修改錯誤

1         for(int temp : list) {
2             if(temp == 2) {
3                 list.add(10);
4             }
5 }

【場景3】會有並發修改錯誤

1         Iterator<Integer> iterator = list.iterator();
2         while(iterator.hasNext()) {
3             if(iterator.next().equals(2)) {
4                 list.add(10);
5             }
6         }

【場景4】沒有並發修改問題

1         ListIterator<Integer> listIterator = list.listIterator();
2 while (listIterator.hasNext()) { 3 if (listIterator.next().equals(2)) { 4 listIterator.add(10); 5 } 6 }

【分析】

  其實ConcurrentModificationException異常的拋出是由於checkForComodification(AbstractList類中)方法的調用引起的。

1     private void checkForComodification() {
2         if (this.modCount != l.modCount)
3             throw new ConcurrentModificationException();
4     }

  而checkForComodification方法的調用發生在Iterator相關api方法中,在調用list的iterator方法會創建一個Itr對象。

技術分享圖片

  在創建會與AbstractList的modCount賦予相同的值, 而在Itr的next方法中會調用checkForComodification

技術分享圖片

  在場景3中,list.add操作更改modCount的值,所以會有並發修改錯誤,而場景1中並沒有使用iterator相關api,add操作雖然修改了modCount但是不會檢查modCount所以沒有並發修改錯誤。
  場景4中,ListItr類add方法

 1         public void add(E e) {
 2             checkForComodification();
 3             try {
 4                 int i = cursor;
 5                 ArrayList.this.add(i, e);
 6                 cursor = i + 1;
 7                 lastRet = -1;
 8                 expectedModCount = modCount;
 9             } catch (IndexOutOfBoundsException ex) {
10                 throw new ConcurrentModificationException();
11             }
12         }

  其中8行:在調用了list.add操作之後,將ListItr中的expectedModCount與AbstractList中的modCount進行了同步,所以在下次調用next也就不會拋出異常了,此時假如以後不調用next或者又重新創建了 ListItr也不會有異常拋出。
  最後場景2並沒有使用Iterator中的api為什麽也拋出了異常了。其實編譯器會將for-each循環代碼編譯為Iterator相關api的調用。為了便於查看編譯後的代碼這裏添加一個“———-”打印。

 1         List<Integer> list = new ArrayList<Integer>();
 2         list.add(1);
 3         list.add(2);
 4         list.add(3);
 5         System.out.println("------------");
 6         for (int temp : list) {
 7             if (temp == 2) {
 8                 list.add(10);
 9             }
10         }

編譯後的字節碼為:

技術分享圖片

所以場景2和場景3是一樣的,也會拋出異常了。

ConcurrentModificationException(並發修改異常)的分析