集合类并发出现juc并发修改异常问题:java.util.ConcurrentModificationException
public class ErrorArray {
public static void main(String[] args) {
// List<String> list = Arrays.asList("a", "b", "c");
// list.forEach(System.out::println);
// 抛出java.util.ConcurrentModificationException异常
List<String> stringArrayList = new CopyOnWriteArrayList<>();
for (int i=0;i<=30;i++) {
new Thread(() -> {
stringArrayList.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(stringArrayList);
},String.valueOf(i)).start();
}
/*
* 故障现象:
* java.util.ConcurrentModificationException
* 导致原因:
* 多线程并发修改出错,一个正在写入,另一个过来抢夺,导致数据不一致异常。并发修改异常
* list的add方法并不是线程安全的
* 解决方案:
1:使用new vector<>();
2:使用Collections.synchronizedList(new ArrayList<>());
3:使用juc的new CopyOnWriteArrayList<>();
* 优化建议(同样的错误不犯第二次):
写时复制
CopyonWrite容器即写时复制的容器。往一个 容器添加元素的时候,不直接往当前容器object[]添加,而是先将当前容器object[ ]进行Copy, .
复制出一-个新的容器0bject[] newElements, 然后新的容器object[] newElements 里添加元素,添加完元素之后,
再将原容器的引用指向新的容器setArray(newElements);。 这样做的好处是可以对CopyOnWrite容器进行并发的读,
而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器
public boolean add(E e)
final ReentrantLock lock = this. lock;
lock. lock();
try{
object[] ele ments = getArray();
int Len = elements. length;
0bject[] newElements = Arrays. copyof(elements, len + 1);
newELements[len] = e;
setArray(newELements);
return true;
finally {
lock. unlock();
}
}
* */
}
}
HashSet也有类似的现象,处理的方式也类似:
public class ErrorSet {
public static void main(String[] args) {
// List<String> list = Arrays.asList("a", "b", "c");
// list.forEach(System.out::println);
// 抛出java.util.ConcurrentModificationException异常
Set<String> list =Collections.synchronizedSet(new HashSet<>());
for (int i=0;i<=30;i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
},String.valueOf(i)).start();
}
/*
* 故障现象:
* java.util.ConcurrentModificationException
* 导致原因:
* 多线程并发修改出错,一个正在写入,另一个过来抢夺,导致数据不一致异常。并发修改异常
* list的add方法并不是线程安全的
* 解决方案:
1:使用juc的new CopyOnWriteArraySet<>();
2:使用Collections.synchronizedSet(new HashSet<>());
*/
}
}
hashSet的底层结构是hashMap,
之所以使用add添加元素是一个值,是因为value在内部已经封装了,是一个空对象。
HashMap也有类似的现象,处理的方式也类似:
public class ErrorArray {
public static void main(String[] args) {
// List<String> list = Arrays.asList("a", "b", "c");
// list.forEach(System.out::println);
// 抛出java.util.ConcurrentModificationException异常
Map<String,String> map= Collections.synchronizedMap(new HashMap<>());
for (int i=0;i<=30;i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
},String.valueOf(i)).start();
}
/*
* 故障现象:
* java.util.ConcurrentModificationException
* 导致原因:
* 多线程并发修改出错,一个正在写入,另一个过来抢夺,导致数据不一致异常。并发修改异常
* list的add方法并不是线程安全的
* 解决方案:
1:使用juc的new ConcurrentHashMap();
2:使用 Collections.synchronizedMap(new HashMap<>());
*/
}
}