List
ArrayList
不是线程安全类,在多线程同时写的情况下,会抛出java.util.ConcurrentModificationException
异常。
private static void listNotSafe() {
List<String> list=new ArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(Thread.currentThread().getName() + "\t" + list);
}, String.valueOf(i)).start();
}
}
解决方法:
- 使用
Vector
(ArrayList
所有方法加synchronized
,太重)。 - 使用
Collections.synchronizedList()
转换成线程安全类。 - 使用
java.concurrent.CopyOnWriteArrayList
(推荐)。
CopyOnWriteArrayList
这是JUC的类,通过写时复制来实现读写分离。比如其add()
方法,就是先复制一个新数组,长度为原数组长度+1,然后将新数组最后一个元素设为添加的元素。
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();
}
}
Set
跟List类似,HashSet
和TreeSet
都不是线程安全的,与之对应的有CopyOnWriteSet
这个线程安全类。这个类底层维护了一个CopyOnWriteArrayList
数组。
private final CopyOnWriteArrayList<E> al;
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
HashSet和HashMap
HashSet
底层是用HashMap
实现的。既然是用HashMap
实现的,那HashMap.put()
需要传两个参数,而HashSet.add()
只传一个参数,这是为什么?实际上HashSet.add()
就是调用的HashMap.put()
,只不过Value被写死了,是一个private static final Object
对象。
Map
HashMap
不是线程安全的,Hashtable
是线程安全的,但是跟Vector
类似,太重量级。所以也有类似CopyOnWriteMap,只不过叫ConcurrentHashMap
。
关于集合不安全类
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class ContainerNotSafeDemo {
public static void main(String[] args) {
listNotSafe();
setNoSafe();
mapNotSafe();
}
private static void mapNotSafe() {
//Map<String,String> map=new HashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
System.out.println(Thread.currentThread().getName() + "\t" + map);
}, String.valueOf(i)).start();
}
}
private static void setNoSafe() {
//Set<String> set=new HashSet<>();
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(Thread.currentThread().getName() + "\t" + set);
}, String.valueOf(i)).start();
}
}
private static void listNotSafe() {
//List<String> list=new ArrayList<>();
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(Thread.currentThread().getName() + "\t" + list);
}, String.valueOf(i)).start();
}
}
}