ArrayList 并发操作 ConcurrentModificationException 异常
1、故障现象
ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException
异常
2、故障代码
public class ArrayListTest {
public static void main(String[] args) {
List<String> lists = new ArrayList<>();
lists.add("a");
lists.add("b");
Iterator<String> iterator = lists.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (next == "b") {
lists.remove(next);
}
}
}
}
异常截图
3、导致原因
通过查看异常,发现异常出现的位置在 java.util.ArrayList类的内部类Itr中的checkForComodification方法中
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
通过查看代码发现如果 modCount和expectedModCount不相等就会导致抛出异常。
modCount是修改记录数,expectedModCount是期望修改记录数,初始化时expectedModCount=modCount。
ArrayList集合的add和remove操作都有对modCount++操作,就会导致expectedModCount和modCount值不相等从而产生ConcurrentModificationException异常
4、解决方案
解决方案1:使用iterator的remove操作替代ArrayList集合自己的remove操作
public class ArrayListTest {
public static void main(String[] args) {
List<String> lists = new ArrayList<>();
lists.add("a");
lists.add("b");
Iterator<String> iterator = lists.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (next == "b") {
iterator.remove();
}
}
}
}
分析:通过查看iterator的remove方法发现,其实还是调用了ArrayList集合的remove方法移除元素,但是会使
expectedModCount=modeCount所以不会抛出ConcurrentModificationException异常
解决方案2:使用JUC concurrent包中CopyOnWriteArrayList并发集合类
public class ArrayListTest {
public static void main(String[] args) {
List<String> lists = new CopyOnWriteArrayList<>();
lists.add("a");
lists.add("b");
Iterator<String> iterator = lists.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (next == "b") {
lists.remove(next);
System.out.println(lists);
}
}
}
}
因为迭代器中没有checkForComodification操作,并且集合的add和remove方法中都通过ReentrantLock加锁保证并发操作下的安全性。
5、优化建议
在对ArrayList集合进行并发操作时尽量使用CopyOnWriteArrayList集合类代替
ArrayList 并发操作 ConcurrentModificationException 异常的更多相关文章
- 深入浅出 Java Concurrency (36): 线程池 part 9 并发操作异常体系[转]
并发包引入的工具类很多方法都会抛出一定的异常,这些异常描述了任务在线程池中执行时发生的例外情况,而通常这些例外需要应用程序进行捕捉和处理. 例如在Future接口中有如下一个API: java.uti ...
- Java并发编程:Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- java集合--java.util.ConcurrentModificationException异常
ConcurrentModificationException 异常:并发修改异常,当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常.一个线程对collection集合迭代,另一个线程对Co ...
- 【转】Java ConcurrentModificationException 异常分析与解决方案--还不错
原文网址:http://www.2cto.com/kf/201403/286536.html 一.单线程 1. 异常情况举例 只要抛出出现异常,可以肯定的是代码一定有错误的地方.先来看看都有哪些情况会 ...
- 【转】Java ConcurrentModificationException异常原因和解决方法
原文网址:http://www.cnblogs.com/dolphin0520/p/3933551.html Java ConcurrentModificationException异常原因和解决方法 ...
- 修改List报ConcurrentModificationException异常原因分析
使用迭代器遍历List的时候修改List报ConcurrentModificationException异常原因分析 在使用Iterator来迭代遍历List的时候如果修改该List对象,则会报jav ...
- (转)Java ConcurrentModificationException异常原因和解决方法
转载自:http://www.cnblogs.com/dolphin0520/p/3933551.html 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会 ...
- 在ConcurrentModificationException异常上的联想
1.什么是ConcurrentModificationException? 大家都听说过快速报错fast-fail吧,fast-fail的发生就是说明发生了ConcurrentModification ...
随机推荐
- 小小知识点(三十)集中式大规模和无小区大规模MIMO
集中式大规模MIMO 同一小区的所有接入点( access point,AP) 布置在同一个基站( base station,BS) 中,并且 AP 之间的间距非常小,这种布置方式称为集中式大规模MI ...
- 小技巧(2) 查询自己博客的SEO(如果违规,请先提醒)
今天,教大家查看自己博客的SEO. 首先,上网站http://seo.chinaz.com/ 接着,在网页输入框中输入自己博客地址(带后面路径,不是cnblogs.com这样的,类似https://w ...
- js去除字符串的前后空白
使用gravatar生成邮箱的头像时需要对邮箱地址哈希化,其中需要去除邮箱地址的首尾空白,查找了一些资料,总结一下 使用 js 提供的函数 trim() trim() 方法会删除一个字符串两端的空白字 ...
- 关于java php go 中AES加解密秘钥长度问题
今天心血来朝,想用go把php中的一个小功能重写一下,但在解密aes加密的数据时碰到了个坑! php的mcrypt拓展(貌似php7.1版本以上不支持了)提供了aes的加解密: 而且php aes 的 ...
- 微信小程序修改checkbox的样式
修改前: 修改后: wxml代码: <checkbox class="checkbox" /> wxss代码: /* checkbox选中钱样式 */ checkbox ...
- Tarjan算法伪代码
伪代码: 栈:当前dfs路径上的点low[x]:x能到达的点中最小的dfn dfs(x,t) 将x入栈 dfn[x]=t low[x]=t for(x,y) i ...
- 【LC_Lesson1】--字符串反转练习
LeetCode算法练习题目一: 给定一个字符串,要求将该字符串反转后输出 努力学习,天天向上.借助LeetCode的题目,练习编码能力,数据结构,以及C++和Python的编码能力. 一. 算法实现 ...
- Manacher 学习
推荐博客 :https://blog.csdn.net/zzkksunboy/article/details/72600679 作用 线性时间解决最长回文子串问题. 思想 Manacher充分利用了回 ...
- Iaas/paas/saas 三种模式分别都是做什么?
任何一个在互联网上提供其服务的公司都可以叫做云计算公司.其实云计算分几层的,分别是Infrastructure(基础设施)-as-a- Service,Platform(平台)-as-a-Servic ...
- win10关闭系统更新 (转载)
一.关闭Windows10系统的自动更新服务 1:使用快捷键Win+R,打开运行 2:输入命令:services.msc,打开系统服务界面 找到Windows Update双击 将启动类型改为[禁用] ...