免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Java ConcurrentModificationException 異常分析與解決方案






一、單線程

1. 異常情況舉例

只要拋出出現(xiàn)異常,可以肯定的是代碼一定有錯誤的地方。先來看看都有哪些情況會出現(xiàn)ConcurrentModificationException異常,下面以ArrayList remove 操作進行舉例:

使用的數(shù)據(jù)集合:
           List myList = new ArrayList();           myList.add( '1');           myList.add( '2');           myList.add( '3');           myList.add( '4');           myList.add( '5');

以下三種情況都會出現(xiàn)異常:
           Iterator it = myList.iterator();            while (it.hasNext()) {                String value = it.next();                 if (value.equals( '3')) {                     myList.remove(value);  // error                }           }                  for (Iterator it = myList.iterator(); it.hasNext();) {                String value = it.next();                 if (value.equals( '3')) {                     myList.remove(value);  // error                }           }                        for (String value : myList) {                System. out.println( 'List Value:'   value);                 if (value.equals( '3')) {                     myList.remove(value);  // error                }           }   

異常信息如下:
Exception in thread 'main' java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)


2. 根本原因

以上都有3種出現(xiàn)異常的情況有一個共同的特點,都是使用Iterator進行遍歷,且都是通過ArrayList.remove(Object) 進行刪除操作。
想要找出根本原因,直接查看ArrayList源碼看為什么出現(xiàn)異常:
public class ArrayList extends AbstractList        implements Cloneable, Serializable, RandomAccess {                         @Override public boolean remove(Object object) {        Object[] a = array;        int s = size;        if (object != null) {            for (int i = 0; i < s; i  ) {                if (object.equals(a[i])) {                    System.arraycopy(a, i   1, a, i, --s - i);                    a[s] = null;  // Prevent memory leak                    size = s;                    modCount  ;  // 只要刪除成功都是累加                    return true;                }            }        } else {            for (int i = 0; i < s; i  ) {                if (a[i] == null) {                    System.arraycopy(a, i   1, a, i, --s - i);                    a[s] = null;  // Prevent memory leak                    size = s;                    modCount  ;  // 只要刪除成功都是累加                    return true;                }            }        }        return false;    }       @Override public Iterator iterator() {        return new ArrayListIterator();    }               private class ArrayListIterator implements Iterator {          ......             // 全局修改總數(shù)保存到當前類中        /** The expected modCount value */        private int expectedModCount = modCount;        @SuppressWarnings('unchecked') public E next() {            ArrayList ourList = ArrayList.this;            int rem = remaining;               // 如果創(chuàng)建時的值不相同,拋出異常            if (ourList.modCount != expectedModCount) {                throw new ConcurrentModificationException();            }            if (rem == 0) {                throw new NoSuchElementException();            }            remaining = rem - 1;            return (E) ourList.array[removalIndex = ourList.size - rem];        }                     ......     }}   

List、Set、Map 都可以通過Iterator進行遍歷,這里僅僅是通過List舉例,在使用其他集合遍歷時進行增刪操作都需要留意是否會觸發(fā)ConcurrentModificationException異常。


3. 解決方案

上面列舉了會出現(xiàn)問題的幾種情況,也分析了問題出現(xiàn)的根本原因,現(xiàn)在來總結(jié)一下怎樣才是正確的,如果避免遍歷時進行增刪操作不會出現(xiàn)ConcurrentModificationException異常。
            // 1 使用Iterator提供的remove方法,用于刪除當前元素            for (Iterator it = myList.iterator(); it.hasNext();) {                String value = it.next();                 if (value.equals( '3')) {                     it.remove();  // ok                }           }           System. out.println( 'List Value:'   myList.toString());                  // 2 建一個集合,記錄需要刪除的元素,之后統(tǒng)一刪除                        List templist = new ArrayList();            for (String value : myList) {                 if (value.equals( '3')) {                     templist.remove(value);                }           }            // 可以查看removeAll源碼,其中使用Iterator進行遍歷           myList.removeAll(templist);           System. out.println( 'List Value:'   myList.toString());                             // 3. 使用線程安全CopyOnWriteArrayList進行刪除操作           List myList = new CopyOnWriteArrayList();           myList.add( '1');           myList.add( '2');           myList.add( '3');           myList.add( '4');           myList.add( '5');           Iterator it = myList.iterator();            while (it.hasNext()) {                String value = it.next();                 if (value.equals( '3')) {                     myList.remove( '4');                     myList.add( '6');                     myList.add( '7');                }           }           System. out.println( 'List Value:'   myList.toString());                      // 4. 不使用Iterator進行遍歷,需要注意的是自己保證索引正常            for ( int i = 0; i < myList.size(); i  ) {                String value = myList.get(i);                System. out.println( 'List Value:'   value);                 if (value.equals( '3')) {                     myList.remove(value);  // ok                     i--; // 因為位置發(fā)生改變,所以必須修改i的位置                }           }           System. out.println( 'List Value:'   myList.toString());

輸出結(jié)果都是:List Value:[1, 2, 4, 5] , 不會出現(xiàn)異常。
以上4種解決辦法在單線程中測試完全沒有問題,但是如果在多線程中呢?


二、多線程

1. 同步異常情況舉例

上面針對ConcurrentModificationException異常在單線程情況下提出了4種解決方案,本來是可以很哈皮的洗洗睡了,但是如果涉及到多線程環(huán)境可能就不那么樂觀了。
下面的例子中開啟兩個子線程,一個進行遍歷,另外一個有條件刪除元素:
          final List myList = createTestData();                new Thread(new Runnable() {                          @Override               public void run() {                    for (String string : myList) {                         System.out.println('遍歷集合 value = '   string);                                              try {                              Thread.sleep(100);                         } catch (InterruptedException e) {                              e.printStackTrace();                         }                    }               }          }).start();                new Thread(new Runnable() {                          @Override               public void run() {                                 for (Iterator it = myList.iterator(); it.hasNext();) {                     String value = it.next();                                      System.out.println('刪除元素 value = '   value);                                      if (value.equals( '3')) {                          it.remove();                     }                                      try {                              Thread.sleep(100);                         } catch (InterruptedException e) {                              e.printStackTrace();                         }                }               }          }).start();

輸出結(jié)果:
遍歷集合 value = 1
刪除元素 value = 1
遍歷集合 value = 2
刪除元素 value = 2
遍歷集合 value = 3
刪除元素 value = 3
Exception in thread 'Thread-0' 刪除元素 value = 4
java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at list.ConcurrentModificationExceptionStudy$1.run(ConcurrentModificationExceptionStudy.java:42)
at java.lang.Thread.run(Unknown Source)
刪除元素 value = 5


結(jié)論:
上面的例子在多線程情況下,僅使用單線程遍歷中進行刪除的第1種解決方案使用it.remove(),但是測試得知4種的解決辦法中的1、2、3依然會出現(xiàn)問題。
接著來再看一下JavaDoc對java.util.ConcurrentModificationException異常的描述:
當方法檢測到對象的并發(fā)修改,但不允許這種修改時,拋出此異常。
說明以上辦法在同一個線程執(zhí)行的時候是沒問題的,但是在異步情況下依然可能出現(xiàn)異常。


2. 嘗試方案

(1) 在所有遍歷增刪地方都加上synchronized或者使用Collections.synchronizedList,雖然能解決問題但是并不推薦,因為增刪造成的同步鎖可能會阻塞遍歷操作。
(2) 推薦使用ConcurrentHashMap或者CopyOnWriteArrayList。


3. CopyOnWriteArrayList注意事項

(1) CopyOnWriteArrayList不能使用Iterator.remove()進行刪除。
(2) CopyOnWriteArrayList使用Iterator且使用List.remove(Object);會出現(xiàn)如下異常:
java.lang.UnsupportedOperationException: Unsupported operation remove
at java.util.concurrent.CopyOnWriteArrayList$ListIteratorImpl.remove(CopyOnWriteArrayList.java:804)

4. 解決方案

單線程情況下列出4種解決方案,但是發(fā)現(xiàn)在多線程情況下僅有第4種方案才能在多線程情況下不出現(xiàn)問題。
           List myList = new CopyOnWriteArrayList();           myList.add( '1');           myList.add( '2');           myList.add( '3');           myList.add( '4');           myList.add( '5');          new Thread(new Runnable() {                           @Override               public void run() {                    for (String string : myList) {                         System.out.println('遍歷集合 value = '   string);                                               try {                              Thread.sleep(100);                         } catch (InterruptedException e) {                              e.printStackTrace();                         }                    }               }          }).start();                 new Thread(new Runnable() {                           @Override               public void run() {                                     for (int i = 0; i < myList.size(); i  ) {                         String value = myList.get(i);                                               System.out.println('刪除元素 value = '   value);                                       if (value.equals( '3')) {                          myList.remove(value);                          i--; // 注意                                                }                                       try {                              Thread.sleep(100);                         } catch (InterruptedException e) {                              e.printStackTrace();                         }                    }               }          }).start();


輸出結(jié)果:
刪除元素 value = 1
遍歷集合 value = 1
刪除元素 value = 2
遍歷集合 value = 2
刪除元素 value = 3
遍歷集合 value = 3
刪除元素 value = 4
遍歷集合 value = 4
刪除元素 value = 5
遍歷集合 value = 5

OK,搞定



三、參考資料

《How to Avoid ConcurrentModificationException when using an Iterator》

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
被問到List 如何一邊遍歷一邊刪除,該怎么回?教你 3 種方法
從ArrayLis遍歷刪除的bug
遍歷并批量刪除容器中元素出現(xiàn)ConcurrentModificationException...
Java 集合源碼解析(1):Iterator
Java提高篇(三四)-----fail-fast機制
HashMap 為什么不能一邊遍歷一邊刪除?
更多類似文章 >>
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服