解釋爲何不能依賴fail-fast

個人觀點
fail-fast是什麼就很少解釋了,應該注意到的是(以ArrayList爲例):modCount位於AbstractList中,java

protected transient int modCount = 0;

並沒有volatile修飾,所以當兩線程是共用同一個cpu時纔會拋出併發修改異常。好比:git

線程1正在用迭代器來讀,此時共用同一個cpu**的線程2來修改list,使得modCount++。因爲共用同一個cpu,那麼所修改的是**同一個緩存中的modCount,這樣使得線程1下一次檢查時發現與指望值不等,便會拋出異常github

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

可是若是線程2用的是不一樣的cpu,而modCount又沒有volatile修飾,那麼線程2對modCount的修改不知道何時纔會寫回主存,也不知道何時線程1纔會從新從主存中讀取modCount。
所以出現併發修改也不必定會拋異常,而其實只要違反規則,單線程照樣會拋出併發修改異常緩存

public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        Iterator iterator=list.iterator();
        while(iterator.hasNext()){
            iterator.next();
            list.add(3);
        }
    }
//    Exception in thread "main" java.util.ConcurrentModificationException
//    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
//    at java.util.ArrayList$Itr.next(ArrayList.java:859)
//    at github.com.AllenDuke.concurrentTest.future.FutureTest.main(FutureTest.java:29)

可是線程用哪一個cpu執行任務是不可知的。併發

所見的網上的答案
注意:這裏異常的拋出條件是檢測到modCount != expectedModCount這個條件。若是集合發生變化時修改modCount值恰好又設置爲了expectedModCount值,則異常不會拋出。所以,不能依賴於這個異常是否拋出而進行併發操做的變成,這個異常只建議用於檢測併發修改的bug。spa

這句話會誤讓人覺得,線程進去修改的時候+1,修改完就-1。但實際上modCount是隻會遞增的,至少在jdk1.8中沒有發現modCount--或是--modCount。利用反射能夠看出並非退出方法就-1,以下:線程

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ArrayList<Integer> list = new ArrayList<>();
        Class c= AbstractList.class;
        Field modCountField = c.getDeclaredField("modCount");
        modCountField.setAccessible(true);
        for (int i = 0; i < 5; i++) {
            list.add(i);
            System.out.println(modCountField.get(list));
        }
    }
//        1
//        2
//        3
//        4
//        5

或者這句話的意思是兩個線程同時+1,這樣的話,根本緣由就和個人觀點一致了。code

相關文章
相關標籤/搜索