解释为什么不能依赖fail-fast

Posted allenduke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解释为什么不能依赖fail-fast相关的知识,希望对你有一定的参考价值。

我的观点
fail-fast是什么就不多解释了,应该注意到的是(以ArrayList为例):modCount位于AbstractList中,

protected transient int modCount = 0;

并无volatile修饰,因此当两线程是共用同一个cpu时才会抛出并发修改异常。比如:

线程1正在用迭代器来读,此时共用同一个cpu**的线程2来修改list,使得modCount++。由于共用同一个cpu,那么所修改的是**同一个缓存中的modCount,这样使得线程1下一次检查时发现与期望值不等,便会抛出异常

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。

这句话会误让人以为,线程进去修改的时候+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,这样的话,根本原因就和我的观点一致了。

以上是关于解释为什么不能依赖fail-fast的主要内容,如果未能解决你的问题,请参考以下文章

有趣的 C++ 代码片段,有啥解释吗? [复制]

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]

Android Dagger片段可以访问AppModule和ActivityModule依赖关系,但不能访问FragmentModule依赖关系

为啥它给我一个错误参数不能为空?

fail-fast机制

需要对特定 R 代码片段的解释