先给出不安全的代码:
public static void main(String[] args) { //List<String> list = Collections.synchronizedList(new ArrayList<>()); List<String> list = new ArrayList<String>(); for(int i = 0; i < 30; i++){ new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(Thread.currentThread().getName()+"\t"+list.toString()); //System.out.println(list.toString()); },"thread: "+i).start(); } }
运行完,控制台报错如下:
报了并发修改异常。
给出解决办法:
1.使用new Vector<>();进行替换(源码内部加锁synchronized线程安全,效率低)
2.使用Collections工具类中的synchronizedList()方法,代码如下:
List<String> list = Collections.synchronizedList(new ArrayList<>());
示例代码:
public static void main(String[] args) { List<String> list = Collections.synchronizedList(new ArrayList<>()); //List<String> list = new ArrayList<String>(); for(int i = 0; i < 30; i++){ new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(Thread.currentThread().getName()+"\t"+list.toString()); //System.out.println(list.toString()); },"thread: "+i).start(); } }
同时Collections还提供了synchronizedMap()、synchronizedSet()等方法。
3.使用JUC中的CopyOnWriteArrayList<>();
new CopyOnWriteArrayList<>();原理:写时复制技术。
CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);。这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器,不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
// 首先:加锁!
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 1、将内容复制了一份
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 2、写入新的内容
newElements[len] = e;
// 3、合并
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}