一、ArrayList线程不安全演示
package com.yuxx.juc;
import java.util.ArrayList;
import java.util.List;
public class ListNotSafeDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
list.add(1);
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
由于没有加任何同步锁,多线程情况下抛出了java..util.ConcurrentModificationException。
打印结果:
二、ArrayList线程不安全解决办法
2.1 new Vector()
Vector类的add、removeElement等方法都加了Synchronized同步锁,所以能够实现线程安全。
演示:
package com.yuxx.juc;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
public class ListNotSafeDemo {
public static void main(String[] args) {
List<Integer> list = new Vector<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
list.add(1);
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
打印结果:
2.2 Collections.synchronizedList(new ArrayList);
Collections.synchronized方法体:
再看ArrayList是否是RandomAccess的实例:
所以Collections.synchonized方法中返回的是new SynchronizedRandomAccessList()。
SynchronizedRadomAccessList是Collections的一个静态内部类,继承了SynchronizedList类,而SynchronizedRadomAccessList本身没有几个实现方法,所以大部分还是使用SynchronizedList方法里的方法。
SynchronizedList的方法里,都有synchronized修饰,保证了线程安全:
2.3 new CopyOnWriteArrayList()
写时复制:CopyOnWrite容器即写时复制容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加。而是先将当前的Object[]进行copy,复制出一个新的容器Object[] newElements。然后新的容器Object[] newElements里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements)。
这样做的好处是,可以对CopyOnWrite容器进行并发的读,而不需要加锁。因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
CopyOnWriteArrayList.add实例方法,就是通过可重入锁锁住增加元素代码,然后先复制集合,写入,重新指向: