解决--java.util.ConcurrentModificationException
程序员文章站
2022-06-23 12:50:04
...
一直都说ArrayList是线程不安全的,只知其话,不懂其意,今天终于碰到了
举例如下:
单线程情况下:—>运行正常
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
list.add(UUID.randomUUID().toString().substring(0, 6));//加入字符串
System.out.println(list);//读取list
}
}
线程不安全,那么好,将ArrayList放入多线程中
举例如下:
多线程情况下:—>运行出异常
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, 6));
System.out.println(list);
}, "A").start();
}
}
如下错误:并发修改异常
原因分析
并发修改异常,指的是在多线程情况下线程争抢同一资源时读写出错。 例如:大巴车开车和顾客上车,大巴车行驶时是不能上乘客的,上乘客的时候大巴车不允许开车,当同时开车并同时上乘客就会出现危险。
查看ArrayList源码如下:
解决思路
- 既然
ArrayList
是线程不安全的,那就使用线程安全的集合,例如使用Vector
- 可不可以让
ArrayList
变成线程安全的 - 既然是读写时出现的错误,是不是可以让读写分开(这一点值得思考 --> 写时复制技术)
解决方案一:使用线程安全的集合
public static void main(String[] args) {
//
List<String> list = new Vector<>();
//
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 6));
System.out.println(list);
}, "A").start();
}
}
解决方案二:将ArrayList
变成线程安全
public static void main(String[] args) {
//
List<String> list = Collections.synchronizedList(new ArrayList<>());
//
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 6));
System.out.println(list);
}, "A").start();
}
}
解决方案三:读写分离–>写时复制技术
public static void main(String[] args) {
//
List<String> list = new CopyOnWriteArrayList<>();
//
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 6));
System.out.println(list);
}, "A").start();
}
}
写时复制技术
1.什么是写时复制技术?
写时复制是指: 在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改。修改完成之后,将指向原来容器的引用指向新的容器(副本容器)。
2.写时复制带来的影响
① 由于不会修改原始容器,只修改副本容器。因此,可以对原始容器进行并发地读。其次,实现了读操作与写操作的分离,读操作发生在原始容器上,写操作发生在副本容器上。
② 数据一致性问题:读操作的线程可能不会立即读取到新修改的数据,因为修改操作发生在副本上。但最终修改操作会完成并更新容器,因此这是最终一致性。
上一篇: Java之JDBC快速入门
下一篇: 那女的单身叫什么