一、不安全示例
ArrayList是java集合框架中一个重要的类,是一个长度可变的集合。在多线程环境下的操作ArrayList是不安全的,会抛出并发修改异常
ConcurrentModificationException。
话不多说,上示例:
在for循环中new了十个线程,每一个线程都向list中添加元素
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
二、解决方案
1.Vector
List<String> list = new Vector<>();
在jdk1.0时,就发布的Vector集合,该集合中的add方法是加synchornized锁的,可以保障线程安全
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
虽然synchornized保证了线程安全,但是大大影响了性能
运行结果:
2.Collections
List<String> list = Collections.synchronizedList(new ArrayList<>());
Collections是所有集合的父类,在Collections中有许多加锁的集合,这里将ArrayList改为Collections.synchronizedList(new ArrayList<>()) 使集合变安全
运行结果:
3.CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList是java.util.concurrent为开发者提供的一个线程安全集合,CopyOnWrite就是写入时复制 。
在多线程环境下对一个list进行操作时,读没有影响,但进行写入操作会发生覆盖问题
CopyOnWrite就是在写入的时候避免覆盖,造成数据问题
CopyOnWriteArrayList比Vector的性能要快
Vector底层add方法用的是synchornized,而CopyOnWriteArrayList底层用的是lock锁
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//copyOf --- > 写入时复制
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
运行结果:
三、HashSet
HashSet也是线程不安全集合,不过HashSet没有像Vector一样加锁的集合。
使HashSet线程安全的方式:
1.Collections.synchronizedSet
Set<String> set = Collections.synchronizedSet(new HashSet<>());
2.CopyOnWriteArraySet
Set<String> set = new CopyOnWriteArraySet<>();