请举例说明集合类是不安全的,面试加分项:你在开发中遇到过哪些问题、遇到过哪些异常?
异常对象: java.util.ConcurrentModificationException
并发修改异常
异常情况:
每次出现错误的情况都不一样,在程序运行正常的情况下,有时空值,有时有值,有时还会报异常
导致原因:
ArrayList集合是线程不安全的,又要读数据,又要写数据
解决方案:
方法一:使用Vector代替ArrayList,因为Vector是线程安全的。
方法二:把一个线程不安全的转化成线程安全的,即List<String> list = Collections.synchronizedList(new ArrayList<>());
方式三:使用高并发解决List<String> list = new CopyOnWriteArrayList<>();
(最重要)
不准回答加锁,因为加锁属于“重复造轮子”,Java已经有了安全集合Vector
分析:
1、使用ArrayList,写入数据时线程不安全,但读取效率高
2、使用Vector,线程安全,但读取效率低
3、使用CopyOnWriteArrayList,读写分离,读取效率高,写入数据线程安全
代码:
package com.monster.ticket.communication;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Monster
* @version v1.0
* @time 03-29-2021 22:55:02
* @description: 请举例说明集合类是不安全的,面试:你在开发中遇到过哪些问题
*/
public class NotSafeDemo {
public static void main(String[] args) {
// List<String> list = new ArrayList<>();
// List<String> list = new Vector<>();
// List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
String str = UUID.randomUUID().toString().substring(0, 8);
list.add(str);
System.out.println(Thread.currentThread().getName() + "\t" + list);
}, String.valueOf(i)).start();
}
}
}
CopyOnWriteArrayList
原理分析
写时复制
Copyonwrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器object[]添加,而是先将当前容器object[ ]进行Copy.复制出一个新的容器object[ ] newElements,然后新的容器object[ ] newELements里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newELements)。这样做的好处是可以对Copyonwrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以Copyonwrite容器也是一种读写分离的思想,读和写不同的容器public boolean add(E e)
源码分析
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
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();
}
}