e^nx

导航

# 多线程:为啥无法避免并发修改异常?(假设有两个线程,线程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遍历,不要用迭代器遍历,就算遍历也不要修改。还是多花时间在应用上,至于一些需要细究的点,有必要的就搞懂,暂时不太重要的先记下来把。继续加油!!!

posted on 2021-01-30 17:32  e^nx  阅读(178)  评论(0编辑  收藏  举报