标签:内部类 read inf 线程 war ransient 并发 .com http
AbstractList类中有一个属性
protected transient int modCount = 0;
api中对它的描述是:
iterator和listIterator方法返回。 如果该字段的值意外更改,迭代器(或列表迭代器)将抛出一个ConcurrentModificationException响应next , remove , previous , set或add操作。 这提供了fail-fast行为,而不是面对在迭代期间的并发修改的非确定性行为我的理解是,modCount表示了当前列表结构被修改的次数,在调用迭代器操作时,则会检查这个值,如果发现已更改,抛出异常
不过需要注意的是transient修饰意味着这个属性不会被序列化,而且modCount并没有被voliate修饰,也就是它不能保证在线程之间是可见的
从ArraytList源码中可以发现,add,remove,clear等方法实现时,均添加了modCount++;操作,例如clear方法:
public void clear() {
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }
在arraylist中调用迭代器是通过内部类实现的:
public Iterator<E> iterator() {
        return new Itr();
    }
在这个内部类中,同样维护了一个类似modCount的变量
int expectedModCount = modCount;
并提供了检测方法
final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
这个检测方法在迭代器中类似next方法里面作为首先需要判断的条件
@SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
我们综合以上,就可以得出,
在使用迭代器遍历arraylist时,会初始化一个和modCount相等的变量,如果在迭代过程中,arraylist中发生了类似add这种改变结构的操作(modCount改变),导致modCount != expectedModCount,那么会抛出一个异常ConcurrentModificationException,即产生fail-fast事件
产生fail-fast有两种原因:
1.单线程情况下,迭代的过程中,调用类似add方法,但是一般不会这样做
     ArrayList<Integer> al = new ArrayList<>();
        for(int i=0;i<10;i++)
            al.add(i);
        Iterator<Integer> it = al.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
            if(!it.hasNext())
                al.add(10);
        }    
2.主要发生在多线程情况下,例如让线程1迭代,线程2修改,就有可能会出现
        final ArrayList<Integer> al = new ArrayList<>();
        for(int i=0;i<10;i++)
            al.add(i);    
        
        new Thread(new Runnable() {            
            @Override
            public void run() {
                Iterator<Integer> it = al.iterator();
                while(it.hasNext()) {
                    System.out.print(it.next()+" ");
                }                
            }
        }).start();        
        new Thread(new Runnable() {                    
                    @Override
                    public void run() {
                        al.remove(6);        
                    }                    
        }).start();
    
如果抛出异常,就类似这个样子:
0 1 Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
    at java.util.ArrayList$Itr.next(ArrayList.java:859)
    at com.rw.importword.utils.Text$1.run(Text.java:19)
    at java.lang.Thread.run(Thread.java:748)
但是也有可能不抛出异常:

根据输出结果来看,al的结构已经改变,但是没有抛出异常
至于原因我想是:
modCount没有被voliate修饰,在线程之间不可见,可能某一个时机,线程2中remove操作改变的modCount值并没有及时写到内存中,线程1中迭代器获取的modCount值仍然是之前的值
由此可以得出,fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。
通过ArrayList对modCount的操作分析fail-fast 机制
标签:内部类 read inf 线程 war ransient 并发 .com http
原文地址:https://www.cnblogs.com/NextLight/p/9592069.html