zoukankan      html  css  js  c++  java
  • # 多线程:为啥无法避免并发修改异常?(假设有两个线程,线程A,线程B),怎么避免并发修改异常?

    多线程:为啥无法避免并发修改异常?(假设有两个线程,线程A,线程B),怎么避免并发修改异常?

    首先要明确你将哪个数据作为共享变量,

    • 是ArrayList对象
    • 迭代器对象iterator

    其次有两种并发修改操作:

    • 通过迭代器的remove修改源集合对象
    • 通过源集合类的方法修改源集合对象

    一.以ArrayList对象作为共享数据的情景:,

    ​ 并发修改操作1:如果在线程体中通过ArrayList生成迭代器对象iterator,此时实际情况有两个迭代器对象iterator,和一个ArrayList共享对象,三个对象。在线程A迭代的过程中,用源集合对象调用方法修改,源集合对象中的modCount+1,在下次线程A迭代器迭代的时候,校验(expectedModCount== modCount),迭代器中的expectedModCount由于还是之前生成迭代器的时候保存的值,校验结果抛出异常,即使线程体上了锁,只要发生了源集合对象调用方法修改,就会出现并发修改异常。

    ​ 并发修改操作2: 如果用的是迭代器的remove方法修改,在一个线程中的确不会有问题,但是由于两个线程共享的ArrayList对象,也就是共享了它的modCount,即使上了锁,线程A迭代完,线程二进入线程体再迭代的时候会发现,线程2中的迭代器中的expectedModCount 不等于modCount,也会触发运行时异常。

    二. 以迭代器作为共享变量的情况。

    比如:iterator作为共享变量
    run{
    	while (iterator.hasNext()){
        System.out.println(iterator.next());
        iterator.remove();  // 迭代器删除最后返回的那个元素
    }
    }
    
    1. 共享:还是假设有两个线程,共享iterator对象,一个源集合对象。

    ​ 并发修改操作1:当线程A用迭代器对象的remove方法修改源集合对象,会使源集合对象中的modCount,迭代器中的expectedModCount 同时改变,在上锁的情况不会发生问题,如果不上锁就可能出现这种非原子操作执行到一半,发生线程切换,线程B中迭代器的expectedModCount 与源集合对象modCount(由于非原子操作,可能出现同步延迟性)不相等,导致发生典型的多线程安全问题。如果上锁,使iterator.remove变成原子操作,则发生线程切换也没有问题

    ​ 并发修改操作2:当线程A使用源集合的对伐修改源集合对象,很简单,只要还有下次迭代,下次迭代的自动判断两个count,自然会抛出并发修改异常。

    如何避免呢?

    单线程: 迭代遍历的时候,就不要使用源集合类修改。

    多线程: 无法避免。

    反思:

    受天明老师影响,他教我们要对细节"敏感",在SE的测试中也发现自己对细节把握不够。今天精神可嘉,但是还是感觉现阶段不太适合去深究太多,要清醒,理性评估一个知识点是否需要扣细节。结论就是:以后开发要注意,多线程,线程体中集合类对象不要用foreach遍历,不要用迭代器遍历,就算遍历也不要修改。还是多花时间在应用上,至于一些需要细究的点,有必要的就搞懂,暂时不太重要的先记下来把。继续加油!!!

    talk is cheap,write and optimize my code。
  • 相关阅读:
    虚拟机通过nat(网络地址转换)可以联网但无法解析域名(解决)
    信息安全系统设计与实现:第三章学习笔记
    电子公文系统-规格需求说明书
    团队作业(二):需求分析
    信息安全系统设计与实现:第十一章学习笔记
    Scala --Actor通信模型概述
    Hadoop项目调优
    大数据生态组件常用服务端口
    HBase性能优化总结
    Hbase架构,组件分析 ,Hbase集群搭建
  • 原文地址:https://www.cnblogs.com/xiongzk/p/fighting.html
Copyright © 2011-2022 走看看