java 集合modcount_jdk源码阅读笔记之java集合框架(三)(modCount)

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public static voidmain(String[] args) {

ArrayList list = new ArrayList();

list.add("foo1");

Iterator 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源码分析代码中①②③④句。

源码:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

/*** An optimized version of AbstractList.Itr*/

private class Itr implements Iterator{int cursor; //下一个要访问元素的下标

int lastRet = -1;//上一此访问元素的下标

int expectedModCount = modCount;//modCount表示ArrayList对象被修改次数,expectedModCount表示期待的修改次数

public booleanhasNext() {return cursor !=size;

}publicE next() {

checkForComodification();int i =cursor;if (i >=size)throw newNoSuchElementException();

Object[] elementData= ArrayList.this.elementData;if (i >=elementData.length)throw newConcurrentModificationException();

cursor= i + 1;return (E) elementData[lastRet =i];

}public voidremove() {if (lastRet < 0)throw newIllegalStateException();

checkForComodification();try{

ArrayList.this.remove(lastRet);

cursor=lastRet;

lastRet= -1;

expectedModCount=modCount;

}catch(IndexOutOfBoundsException ex) {throw newConcurrentModificationException();

}

}final voidcheckForComodification() {if(modCount != expectedModCount)//表示如果期待修改次数与实际修改次数不等 则抛ConcurrentModificationException异常

throw newConcurrentModificationException();

}

}

View Code

分析:

第①句调用iterator(),

public Iterator iterator() {

return new Itr();

}

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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,保证线程之间修改的可见性。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值