我是靠谱客的博主 高大小懒虫,最近开发中收集的这篇文章主要介绍错误笔记-ConcurrentModificationException,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

错误笔记-ConcurrentModificationException

            • 1)故障现象
            • 2)导致原因
            • 3)解决方案
            • 4)优化建议

1)故障现象

在使用多线程操作ArraryList,HashSet,HashMap等线程不安全集合的时候,会出现如下报错:

java.util.ConcurrentModificationException

这个报错信息的意思为并发修改异常
代码如下

public class NotSafe {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,4));
System.out.println(list);
},"A").start();
}
}
}
2)导致原因
1)没加锁
2)迭代的时候数据变化,导致希望的集合个数,和真实的集合个数不一致,所以会导致报错

简单的说就是两个线程在对资源操作的时候进行了争抢,导致A线程在读取集合数据输出的时候,B线程又往集合中新增了数据,导致了A线程读取的集合个数和真实个数不一样,导致报错。
可以查看这一段源码,这段源码作用是,指针遍历集合

@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];
}

主要看这个方法checkForComodification()
这个方法是用来判断modCount这个值是否和所期望的modCount相同,如果不相同就会抛出ConcurrentModificationException这个异常
modCount是什么根据浏览其他资料,这个数值其实是修改次数,所以源码是通过判断这个集合的修改次数来判断这个集合是否在读取的时候被其他线程修改

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
3)解决方案

有三种解决方案
1)使用线程安全的集合类,如Vector

List<String> list = new Vector<>();

2)使用集合工具类来给ArraryList穿上层带锁的衣服

List<String> list = Collections.synchronizedList(new ArrayList<>());
Set<String> set = Collections.synchronizedSet(new HashSet<>());
Map<String,Object> map = Collections.synchronizedMap(new HashMap<>());

3)使用juc提供的实现类

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
List<String> list = new CopyOnWriteArrayList<>();
Set<String> set = new CopyOnWriteArraySet<>();
Map<String,Object> map = new ConcurrentHashMap<>();

这三个实现类,核心是写时复制技术
用人话说就是多线程读的时候是同一个集合,如果其中一个线程需要修改集合数据,就需要在集合复制出来的新集合里面修改,然后在将旧集合覆盖,然其他集合来读取新的集合,从而实现线程的安全,简单的说就是读写分离
源码如下


public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
4)优化建议

以上三种优化方案,建议使用juc提供的实现类,这样可以优化集合操作的性能,不会因为修改导致读取集合操作的性能下降

最后

以上就是高大小懒虫为你收集整理的错误笔记-ConcurrentModificationException的全部内容,希望文章能够帮你解决错误笔记-ConcurrentModificationException所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部