ArrayList底层jdk源码之modCount、expectedModCount解读

为了说明本次的内容,先看一下示例代码:

 public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        Iterator<String> it = list.iterator();//1
        while (it.hasNext()) {//2
            String str = it.next();//3
            if (str.equals("apple")) {
                list.add("banana");//4
            }
        }
    }

运行结果:
在这里插入图片描述
把目光聚焦在ConcurrentModificationException上,然后关于modCount的用处就会浮出水面。

结合java.util.ArrayList.Itr源码分析代码中①②③④句。

 public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }

        @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];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
         final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
   }

分析:

第①句调用iterator(),

public Iterator<E> iterator() {
        return new Itr();
    }

调用了new Itr(),生成Itr类(迭代器)。此时会给Itr的三个参数初始化。

		int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

此时expectedModCount == modCount == 1(因为list调动了add方法,add方法会对modCount实现++操作)

第②句调用下面hasNext()方法,返回下一个要访问元素的下标cursor,因为是第一次循环,所以cursor为0;

第③句调用next()方法,正常取值,取到第一个元素"foo1";

第④句调用add()方法,成功给list添加元素。注意,在调用add方法的时候,有modCount++。所有此时,modCount2,expectedModCount1。

至此,第一次循环走完。虽然list本来只有一个元素,但后来又添加了"foo2"元素。所以开始第二次循环:

第②句调用下面hasNext()方法,返回下一个要访问元素的下标cursor,第二次循环,所以cursor为1;

第③句调用next()方法,注意,在next()方法中第一句话就是调用checkForComodification();由于modCount(2) != expectedModCount(1),所以就抛了异常。

抛异常的原因已分析明白,那么这样做的意义是什么?试想一下,如果没有modCount,又会有什么影响呢?

我们知道 java.util.ArrayList不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了list,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对ArrayList 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 list:注意到 modCount 声明为 volatile,保证线程之间修改的可见性。

所以在这里和大家建议,当大家遍历那些非线程安全的数据结构时,尽量使用迭代器。归根结底,是从线程安全的角度考虑。

发布了23 篇原创文章 · 获赞 15 · 访问量 6281

猜你喜欢

转载自blog.csdn.net/xiaoleilei666/article/details/102881314