单线程也可能引发"并发"访问异常

不要以为只有多线程才有并发访问问题,其实单线程也有。举个例子,对于集合,相信大家经常碰到下面这种异常:

  1. java.util.ConcurrentModificationException
  2. atjava.util.AbstractList$Itr.checkForComodification(AbstractList.java:449)
  3. atjava.util.AbstractList$Itr.next(AbstractList.java:420)

这个异常是由于并发修改集合元素引起的,大家第一个反应多半是多线程问题,结果可能怎么也找不出问题。这里我就模拟一下单线程引发这个并发问题的例子。

  1. ArrayList<String>list=newArrayList<String>();
  2. list.add("1");
  3. list.add("2");
  4. list.add("3");
  5. list.add("4");
  6. list.add("6");
  7. for(Stringm:list){
  8. System.out.println(m);
  9. list.add("7");//lookhere,problempoint
  10. }

上面这个例子只要一执行就会出现异常。为什么呢?

Iterator模式是用于遍历集合类的标准访问方法,我们来看看集合AbstracyList如何创建Iterator。首先AbstractList定义了一个内部类(inner class):

  1. privateclassItrimplementsIterator{
  2. ...
  3. }

而iterator()方法的定义是:

  1. publicIteratoriterator(){
  2. returnnewItr();
  3. }

因此客户端不知道它通过Iterator it = a.iterator();所获得的Iterator的真正类型。

现在我们关心的是这个申明为private的Itr类是如何实现遍历AbstractList的:

  1. privateclassItrimplementsIterator{
  2. intcursor=0;
  3. intlastRet=-1;
  4. intexpectedModCount=modCount;

Itr类依靠3个int变量(还有一个隐含的AbstractList的引用)来实现遍历,cursor是下一次next()调用时元素的位置,第一次调用next()将返回索引为0的元素。lastRet记录上一次游标所在位置,因此它总是比cursor少1。

变量cursor和集合的元素个数决定hasNext():

  1. publicbooleanhasNext(){
  2. returncursor!=size();
  3. }

方法next()返回的是索引为cursor的元素,然后修改cursor和lastRet的值:

  1. publicObjectnext(){
  2. checkForComodification();
  3. try{
  4. Objectnext=get(cursor); //注意这里:得到下一个元素
  5. lastRet=cursor++;
  6. returnnext;
  7. }catch(IndexOutOfBoundsExceptione){
  8. checkForComodification();
  9. thrownewNoSuchElementException();
  10. }
  11. }

expectedModCount表示期待的modCount值,用来判断在遍历过程中集合是否被修改过。AbstractList包含一个 modCount变量,它的初始值是0,当集合每被修改一次时(调用add,remove等方法),modCount加1。因此,modCount如果不 变,表示集合内容未被修改。

  1. publicEget(intindex){
  2. rangeCheck(index); //检查范围
  3. checkForComodification();//注意这里:检查是否有被修改
  4. returnl.get(index+offset);
  5. }

Itr初始化时用expectedModCount记录集合的modCount变量,此后在必要的地方它会检测modCount的值:

  1. finalvoidcheckForComodification(){
  2. if(modCount!=expectedModCount)
  3. thrownewConcurrentModificationException();
  4. }

如果modCount与一开始记录在expectedModeCount中的值不等,说明集合内容被修改过,此时会抛出ConcurrentModificationException。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值