乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Java ConcurrentModificationException 異常分析與解決方案

       圖書館送餐人員 2016-03-16




      \


      一、單線程

      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ǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多