最近写代码时遇到一个exception:

ConcurrentModificationException

这个问题经常发生在对集合类的操作中,文档上原文描述是这样的

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.

For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected. Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.

Note that fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throwConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.

我自己的理解就是你不可以在遍历一个集合类的同时去改变它的大小,这样做是不对的,典型的就是例如这样:

for(String str:set){
set.remove(str);
}

解决方案通常是使用Iterator的remove方法,我对几个常用的集合类都试了,是可以的。这样做的原理在于:

 Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。

  所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

所以只要记住“不在遍历一个集合类的同时去改变它的大小”,基本就可以理解了,这和是否多线程其实并没有关系,而且经过我的试验,改变集合中的内容是可以的,只要大小不变就行。以下是我的测试代码:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;public class TestCollection {class A{A(String v){value = v;}String value = null;@Overridepublic String toString() {// TODO Auto-generated method stubreturn value != null?value:"null";}}/*** @param args*/public static void main(String[] args) {TestCollection t = new TestCollection();t.testMap();t.testSet();t.testList();}private void testList() {ArrayList<String> list = new ArrayList<String>();list.add("1");list.add("2");list.add("3");list.add("4");list.add("5");list.add("6");list.add("7");System.out.print("before~~~"+list);//+++//这样写会错,ConcurrentModificationException
//      for(String str:set){
//          set.remove(str);
//      }//---Iterator i = list.iterator();while(i.hasNext()){System.out.print(""+i.next());i.remove();}System.out.print("after~~~"+list);}private void testSet() {Set<String> set = new HashSet<String>();set.add("1");set.add("2");set.add("3");set.add("4");set.add("5");set.add("6");set.add("7");System.out.print("before~~~"+set);//+++//这样写会错,ConcurrentModificationException
//      for(String str:set){
//          set.remove(str);
//      }//---Iterator i = set.iterator();while(i.hasNext()){System.out.print(""+i.next());i.remove();}System.out.print("after~~~"+set);}private void testMap() {// TODO Auto-generated method stubMap<String, A> stringMap = new HashMap<String, A>();stringMap.put("1", new A("Monday"));stringMap.put("2", new A("Tuesday"));stringMap.put("3", new A("Wensday"));stringMap.put("4", new A("Wednesday"));stringMap.put("5", new A("Friday"));stringMap.put("6", new A("Saturday"));stringMap.put("7", new A("Sunday"));System.out.print("before~~~"+stringMap);Iterator i = stringMap.keySet().iterator();while(i.hasNext()){String key  = (String) i.next();System.out.print(key);//执行下面这句会报错
//          stringMap.remove(key);//但是下面这句不会报错
//          stringMap.get(key).value = "Test";i.remove();}System.out.print("after~~~"+stringMap);}}

ConcurrentModificationException相关推荐

  1. 理解和解决Java并发修改异常ConcurrentModificationException(转载)

    原文地址:https://www.jianshu.com/p/f3f6b12330c1 理解和解决Java并发修改异常ConcurrentModificationException 不知读者在Java ...

  2. 一种隐蔽性较高的Java ConcurrentModificationException异常场景

    前言 在使用Iterator遍历容器类的过程中,如果对容器的内容进行增加和删除,就会出现ConcurrentModificationException异常.该异常的分析和解决方案详见博文<Jav ...

  3. 使用迭代器遍历List的时候修改List报ConcurrentModificationException异常原因分析

    在使用Iterator来迭代遍历List的时候如果修改该List对象,则会报java.util.ConcurrentModificationException异常,下面看一个例子演示: 1 packa ...

  4. java.util.ConcurrentModificationException(并发修改错...

    为什么80%的码农都做不了架构师?>>>    public class ConcurrentModificationExceptionextends RuntimeExceptio ...

  5. 出现 java.util.ConcurrentModificationException 时的解决办法

    for (int i=0; i<list.size(); i++) { // 只有在这种方式遍历集合时,同时对集合增加.删除才不会影响. // list.remove(list.get(i)); ...

  6. HashMap中ConcurrentModificationException异常解读

    HashMap中ConcurrentModificationException异常解读 参考文章: (1)HashMap中ConcurrentModificationException异常解读 (2) ...

  7. java.util.ConcurrentModificationException异常原因及解决方法

    java.util.ConcurrentModificationException异常原因及解决方法 参考文章: (1)java.util.ConcurrentModificationExceptio ...

  8. Java ConcurrentModificationException异常原因和解决方法

    Java ConcurrentModificationException异常原因和解决方法 参考文章: (1)Java ConcurrentModificationException异常原因和解决方法 ...

  9. 遍历Collection,避免在循环中删除对象时避免ConcurrentModificationException

    我们都知道,由于ConcurrentModificationException您无法执行以下操作: for (Object i : l) {if (condition(i)) {l.remove(i) ...

  10. 非线程安全类ArrayList出现异常:java.util.ConcurrentModificationException

    今天执行了一段<图解多线程设计模式>中的代码,结果抛出了如下的异常: Exception in thread "ReaderThread" java.util.Conc ...

最新文章

  1. Android studio | From Zero To One ——简单布局(View和LinearLayout)
  2. mysql三学习sql声明学习
  3. 图论--SCC强连通缩点--Tarjan
  4. 分布式架构--基本思想汇总
  5. 第四次作业 孙保平034 李路平029
  6. Python实现淘宝秒杀聚划算自动提醒源码
  7. C++:vector二维数组初始化
  8. linux自动ping脚本,linux 自动ping ip的shell脚本
  9. Java 全栈工程师进阶路线图
  10. gitbub基本操作
  11. ios16更新了什么内容 ios16更新内容汇总
  12. 通过js引入本地图片地址
  13. (六)1609.4协议详解
  14. jquery鼠标经过水平180度翻转效果
  15. kali linux安装upupoo_Kali Linux 下载、引导、安装
  16. 决定迭代次数的两种效应
  17. 对象存储OSS之阿里云OSS介绍及开通
  18. 刷爆朋友圈的虚幻引擎5,会给我们带来哪些影响?
  19. js第三章简答题5(制作如图所示的Tab切换效果)
  20. 移动增值业务平台解决实例

热门文章

  1. Excel 数字转化成以文本形式存储的数字
  2. android应用程序耗电,Android的十大耗电量应用程序,在软件中排名第一
  3. 面试问题如何预防xss攻击
  4. c语言switch函数的作用,c语言的switchcase语句
  5. 遗传算法流程概述与简单实例认知
  6. CSS尺寸与文本讲解。HTML、CSS笔记(四)。
  7. Mac网络正常但是所有浏览器无法上网问题解决
  8. Coding(码市)教程(一):基础配置
  9. 【论文】三维坐标下TDOA之chan算法
  10. 如何把微信删除的聊天记录恢复?试试这两个简单有效的方法