Exception in thread "Thread-1" java.util.ConcurrentModificationException 異常緣由和解決方法

基本上全部的集合類都會有一個叫作快速失敗的校驗機制,當一個集合在被多個線程修改並訪問時,就會出現ConcurrentModificationException 校驗機制。它的實現原理就是咱們常常提到的modCount修改計數器。若是在讀列表時,modCount發生變化則會拋出ConcurrentModificationException異常。這與線程同步是兩碼事,線程同步是爲了保護集合中的數據不被髒讀、髒寫而設置的。java


一、單線程環境下的異常重現安全

    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2) {
                list.remove(integer);
                //iterator.remove();   //正確寫法
            }
        }
    }

  在while(iterator.hasNext()) 循環遍歷時,只容許刪除ArrayList 內部的  elementData[ ] 的最後一個元素,而不容許從中間刪除。
  在 iterator.next()  的源碼中,會首先執行方法:checkForComodification();  該方法:多線程

     final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

  只有當兩個變量值相等時纔不會報錯。而 list.add(2)操做和 list.remove(integer); 操做會使 modCount++;   可是變量 expectedModCount 是內部類的 Itr 子類 的 變量,該子類的實例化方法是:list.iterator();  實例對象時賦初始值:
int expectedModCount = modCount;   因此,不容許在 while(iterator.hasNext())  循環方法內 有list.add(2)操做和 list.remove(integer);操做,不然會致使expectedModCount != modCount ,進而致使  throw new ConcurrentModificationException();併發


二、多線程下的問題重現:
public class Vector {
    public static void main(String[] args) {
        final List<String> tickets = new ArrayList<String>();
        for(int i=0;i<100000;i++){
            tickets.add("火車票"+i);
        }
        Thread returnThread = new Thread(){
            @Override
            public void run() {
               while (true){
                   tickets.add("車票"+ new Random().nextInt());
               }
            };
        };
        Thread saleThread = new Thread(){
            @Override
            public void run() {
                for (String ticket : tickets){
                    tickets.remove(0);
                }
            };
        };
        returnThread.start();
        saleThread.start();
    }
}

  

  有可能有朋友說ArrayList是非線程安全的容器,換成Vector就沒問題了,實際上換成Vector仍是會出現這種錯誤。dom

  緣由在於,雖然Vector的方法採用了synchronized進行了同步,可是實際上經過Iterator訪問的狀況下,每一個線程裏面返回的是不一樣的iterator,也便是說expectedModCount是每一個線程私有。倘若此時有2個線程,線程1在進行遍歷,線程2在進行修改,那麼頗有可能致使線程2修改後致使Vector中的modCount自增了,線程2的expectedModCount也自增了,可是線程1的expectedModCount沒有自增,此時線程1遍歷時就會出現expectedModCount不等於modCount的狀況了。ide

  所以通常有2種解決辦法:線程

  1)在使用iterator迭代的時候使用synchronized或者Lock進行同步;對象

  2)使用併發容器CopyOnWriteArrayList代替ArrayList和Vector。blog

相關文章
相關標籤/搜索