zoukankan      html  css  js  c++  java
  • jdk源码阅读笔记之java集合框架(三)(modCount)

    为了说明白本文的主角,先来一段示例代码:

    public static void main(String[] args) {
      ArrayList<String> list = new ArrayList<String>();
      list.add("foo1");
      Iterator<String> iterator = list.iterator();//
      while (iterator.hasNext()) {//
       String str = iterator.next();//
       if (str.equals("foo1"))
        list.add("foo2");//
      }
     }
    View Code

    运行结果:

    Exception in thread "main" java.util.ConcurrentModificationException

    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)

    at java.util.ArrayList$Itr.next(ArrayList.java:831)

    at collections.tt.main(tt.java:20)

    把目光聚焦在ConcurrentModificationException上,然后关于modCount的用处就会浮出水面。

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

    源码:

    /**
         * An optimized version of AbstractList.Itr
         */
        private class Itr implements Iterator<E> {
         int cursor; // 下一个要访问元素的下标
         int lastRet = -1;// 上一此访问元素的下标
         int expectedModCount = modCount;//modCount表示ArrayList对象被修改次数,expectedModCount表示期待的修改次数
     
            public boolean hasNext() {
                return cursor != size;
            }
     
         
            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)//表示如果期待修改次数与实际修改次数不等 则抛ConcurrentModificationException异常
                    throw new ConcurrentModificationException();
            }
        }
    View Code

    分析:

    第①句调用iterator(),

      public Iterator<E> iterator() {

            return new Itr();

        }

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

    int cursor; // 下一个要访问元素的下标
         int lastRet = -1;// 上一此访问元素的下标
         int expectedModCount = modCount;//modCount表示ArrayList对象被修改次数,expectedModCount表示期待的修改次数
    View Code

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

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

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

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

     

    至此,第一次循环走完。虽然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,保证线程之间修改的可见性。

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

     

     

  • 相关阅读:
    中国式关系
    太太万岁
    matlab记录运行时间命令
    matlab读xls数据
    matlab,xls转换为mat文件
    matlab里plot设置线形和颜色
    matlab里plot画多幅图像、设置总标题、legend无边框
    matlab显示图像的横纵坐标
    去掉matlab图片空白边缘
    matlab显示原图和灰度直方图
  • 原文地址:https://www.cnblogs.com/jw93/p/6845825.html
Copyright © 2011-2022 走看看