单线程也可能引发"并发"访问异常
2009-01-06 15:40
363 查看
不要以为只有多线程才有并发访问问题,其实单线程也有。举个例子,对于集合,相信大家经常碰到下面这种异常:
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。
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。
相关文章推荐
- 单线程也可能引发"并发"访问异常
- C# 引用其他命名空间的公共变量时的警告:由于"***"是引用封送类的字段,访问上面的成员可能导致运行时异常
- 计算机无法访问,您可能没有权限使用网络资源.请与这台服务器的管理员联系"的解决办
- 最新升级的火狐38.0.6识别ajax调用返回的""空值可能有异常。
- Window访问Linux共享"您可能没有权限使用网络资源"问题解决
- 正在创建模型,此时不可使用上下文“的解决办法。 正在创建模型,此时不可使用上下文。如果在 OnModelCreating 方法内使用上下文或如果多个线程同时访问同一上下文实例,可能引发此异常。请注意不
- C#开发日志[2013-12-5]创建Bitmap引发"参数无效"异常
- 初始化引发的"StackOverflowError" 异常
- 黑马程序员--07.集合框架--并发访问异常理解:一个单线程程序的多线程运行思想【个人总结】
- asp.net访问access 发生了未处理的异常 "操作必须使用一个可更新的查询"错误
- EF异常‘在创建模型,此时不可使用上下文。如果在 OnModelCreating 方法内使用上下文或如果多个线程同时访问同一上下文实例,可能引发此异常。 请注意不保证 DbContext 的实例成员’
- EF(Entity Framework)发生错误”正在创建模型,此时不可使用上下文“的解决办法。 正在创建模型,此时不可使用上下文。如果在 OnModelCreating 方法内使用上下文或如果多个线程同时访问同一上下文实例,可能引发此异常。请注意不保证 DbContext 的实例成员和相关类是线程安全的。 临时解决了这个问题,在Context的构造函数中,禁用了自动初始化:
- 关于c# 引发了"system.indexOutofRangeException"类型异常,该怎么处理
- [转] "计算机无法访问,您可能没有权限使用网络资源.请与这台服务器的管理员联系以查明您是否有访问权限" 解决方法
- 窗体上有控件时关闭窗体引发bResult = m_pCtrlSite->DestroyControl();访问异常
- 您可能没有权限使用网络资源,请......您是否有访问权限"的解决办法
- 都是"后退"惹得祸-------关于浏览器"后退"在项目中引发的惨剧
- string.Format出现异常"输入的字符串格式有误"的解决方法
- {"当 IDENTITY_INSERT 设置为 OFF 时,不能向表 'OrderList' 中的标识列插入显式值"}-异常处理方案
- 在与sap系统集成时遇到的问题.sap系统发布一webservice,java(xfire)作为客户端调用,调用时抛出如下异常: Wrong Content-Type and empty HTTP-Body received: ("HTTP Code 20