您的位置:首页 > 其它

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

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

java.util.ConcurrentModificationException

at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:449)

at java.util.AbstractList$Itr.next(AbstractList.java:420)

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

ArrayList<String> list=new ArrayList<String>();

list.add("1");

list.add("2");

list.add("3");

list.add("4");

list.add("6");

for(String m:list){

System.out.println(m);

list.add("7");//look here ,problem point

}

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

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

private class Itr implements Iterator {

...

}

而iterator()方法的定义是:

public Iterator iterator() {

return new Itr();

}

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

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

private class Itr implements Iterator {

int cursor = 0;

int lastRet = -1;

int expectedModCount = modCount;

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

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

public boolean hasNext() {

return cursor != size();

}

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

public Object next() {

checkForComodification();

try {

Object next = get(cursor); //注意这里:得到下一个元素

lastRet = cursor++;

return next;

} catch(IndexOutOfBoundsException e) {

checkForComodification();

throw new NoSuchElementException();

}

}

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

public E get(int index) {

rangeCheck(index); //检查范围

checkForComodification();//注意这里:检查是否有被修改

return l.get(index+offset);

}

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

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

如果modCount与一开始记录在expectedModeCount中的值不等,说明集合内容被修改过,此时会抛出ConcurrentModificationException。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐