基本上全部的集合類都會有一個叫作快速失敗的校驗機制,當一個集合在被多個線程修改並訪問時,就會出現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