`
javahigh1
  • 浏览: 1262954 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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

  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。


分享到:
评论

相关推荐

    多线程与单线程的对比(采用CreateThread)

    3. **竞态条件**:如果两个或更多线程同时访问和修改同一数据,可能会导致竞态条件,从而引发错误的结果。 4. **死锁**:当两个或更多线程相互等待对方释放资源时,可能会发生死锁。设计线程间的资源获取顺序以避免...

    多线程并发执行任务

    - **同步机制**:当多个线程访问共享资源时,可能会引发数据不一致的问题。Java提供了`synchronized`关键字来实现线程同步,确保同一时刻只有一个线程执行特定代码块。 - **死锁**:如果两个或多个线程相互等待对方...

    C++并发编程实践 C++ Concurrency in Action

    当多个线程访问同一资源时,可能会引发竞态条件(race condition),导致不可预测的行为。 - **互斥量**:`std::mutex`用于保护临界区,确保任何时候只有一个线程可以访问受保护的数据。 - **读写锁**:如果多个...

    数据库并发测试.pdf

    2. **多服务程序并行**:即使服务程序本身是单线程的,但若有多套服务程序同时运行,每套服务程序都有可能独立访问数据库,同样会引发并发问题。 **二、数据库并发测试方法** 1. **模拟用户并发测试**:通过测试...

    多线程应用实例

    在单线程程序中,所有的任务都是顺序执行的,而多线程则允许多个任务同时进行,提高了程序的并发性和执行效率。在Java、C++、Python等主流编程语言中,都提供了对多线程的支持。 **线程的创建与管理** 1. **创建...

    多线程计算器

    7. **线程安全的数据结构**:某些数据结构如ConcurrentHashMap、AtomicInteger等是线程安全的,它们在设计上就考虑了多线程环境下的并发访问。 8. **死锁与竞态条件**:多线程环境下,必须预防死锁(多个线程互相...

    多线程与异常处理(共71张PPT)精选.pptx

    - **同步**:为了避免多线程环境下可能出现的竞态条件,Java提供了synchronized关键字,用于控制对共享资源的访问,防止数据不一致。 - **死锁**:当两个或更多线程互相等待对方释放资源而无法继续执行时,就会...

    如何使用Python多线程测试并发漏洞

    在并发测试中,通常会创建多个线程来模拟高并发访问。 5. **收集结果与分析**: 在多线程环境中,线程间的通信和数据交换可能引发问题,因此你需要记录每个线程的执行结果,以便分析潜在的并发漏洞。 在实际测试中...

    Java DateFormat并发实现

    `DateFormat`类的设计允许它根据地区设置生成和解析日期字符串,但它在内部使用了一些可变状态,例如解析和格式化模式,这些状态在多个线程同时访问时可能引发问题。例如,一个线程可能正在修改模式,而另一个线程...

    基于Java内存模型的并发程序模型检测.pdf

    本文提出了一种新的并发程序模型检测方法,该方法构建了一个完整的模型,包括多线程间的交互以及单线程内部的“out of order”执行。通过使用模型检测工具,对所有可能的执行路径进行穷举检测,以发现那些传统技术...

    【ASP.NET编程知识】.net core并发下线程安全问题详解.docx

    在.NET Core并发环境下,线程安全问题是一个关键的议题,特别是在ASP.NET Core的WebAPI项目中,由于高并发请求的特性,可能导致多个线程同时访问共享资源,从而引发各种异常。文档中提到的问题主要集中在数据库上...

    多线程调用webservices

    例如,如果多个线程同时访问共享资源,可能引发数据竞争。为解决这些问题,可以使用锁(Mutex, Semaphore)、线程同步原语(Monitor)或线程安全的数据结构(如ConcurrentQueue, ConcurrentDictionary)。 七、异常...

    易语言多线程传递多参数

    在单线程中,我们可以直接通过变量或参数传递数据。但在多线程环境中,由于线程间的并发执行,直接传递可能会引发数据不一致的问题。易语言提供了一种安全的方式来传递参数,即通过“线程参数”数据结构。创建线程时...

    bcb 简单多线程实例

    2. **线程同步**: 当多个线程访问同一资源时,可能引发竞态条件,导致数据不一致。BCB提供了几种同步机制,如TSemaphore、TMutex和TCriticalSection。例如,使用TCriticalSection可以确保同一时间只有一个线程能访问...

    多线程聊天程序多线程聊天程序多线程聊天程序

    2. **线程同步与互斥**:在多线程环境中,数据共享可能会导致竞态条件,即多个线程同时访问和修改同一数据,可能引发错误。为此,我们需要使用锁、信号量、条件变量等机制来确保线程同步,避免数据不一致。例如,在...

    delphi_多线程

    - **单线程与多线程**:单线程程序只有一个执行流,而多线程则有多个执行流,可以在同一进程中并发运行。 - **线程的优势**:多线程可以提高CPU利用率,改善用户体验,如在后台下载数据的同时,用户仍能进行其他...

    vb多线程程序

    在单线程应用中,所有操作都按照顺序执行,而多线程则能并发执行任务,提升整体性能。 VB 6.0虽然不是设计用来处理多线程的,但它依然提供了创建和管理线程的能力,主要通过Microsoft Windows API(应用程序接口)...

    VB 多线程模块案例

    在这个文件中,你可能会找到如何创建、启动、停止线程,以及如何处理线程异常、线程优先级设置、线程池使用等相关代码示例。 总之,这个"VB 多线程模块案例"将为你提供一个深入理解VB中多线程编程的平台,包括线程...

    Java 多线程与并发(14-26)-JUC集合- CopyOnWriteArrayList详解.pdf

    在非并发集合如`ArrayList`中,当多个线程共享集合时,如果一个线程修改了集合的状态(例如添加或删除元素),而此时其他线程正在遍历这个集合,那么后者可能会遇到不一致的数据结构,甚至抛出`...

    Java多线程的总结

    在多线程环境下,数据共享可能会引发数据不一致的问题。Java提供了多种同步机制,如synchronized关键字、volatile变量、Lock接口(ReentrantLock、ReadWriteLock等)和Semaphore信号量。synchronized提供互斥访问,...

Global site tag (gtag.js) - Google Analytics