面试系列----集合类线程安全问题
面试系列----集合类线程安全问题
众所周知,ArrayList是一个线程不安全的。为什么线程不安全呢?线程不安全会出现什么问题呢?
看下面代码:
public class NotSafeDemo03 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread( () -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(Thread.currentThread().getName()+" "+list);
},String.valueOf(i)).start();
}
}
上面的程序会出现异常,java.util.ConcurrentModificationException异常。这个就是并发修改产生的异常。
出现这个问题该怎么解决呢?
方案一:加锁使用Vector类,在Vector中是加了锁的,是线程安全的。这样子就不会出现并发修复异常,但是加锁以后会影响并发的效率。
方案二:同样使用一个加了锁的Arraylist,解决并发修改异常,使用Collections.synchronizedList(new ArrayList<>());创建一个带锁的ArrayList,同样有着影响并发效率的问题
方案三:采用写时复制copyOnwrite,写时复制 copyOnWrite 容器即写时复制的容器,往容器添加元素的时候,不直接往当前容器object[]添加,而是先将当前容器object[]进行,copy 复制出一个新的object[] newElements 然后向新容器object[] newElements 里面添加元素 添加元素后,再将原容器的引用指向新的容器 setArray(newElements);这样的好处就是可以对CopyOnWrite容器进行并发的读,而不加锁。因为当前容器不会添加任何元素,所以CopyOnwrite容器也是一种读写分离的思想,读和写不同的容器。
采用CopyOnWriteArrayList类中的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();
}
}
所以刚开始的出现的异常问题可以相应的解决:
public class NotSafeDemo03 {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread( () -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(Thread.currentThread().getName()+" "+list);
},String.valueOf(i)).start();
}
}
这里多说一点就是比如HashSet和HashMap都是线程不安全的,这里也有相应的保证线程安全的类。
Set<String> set = new CopyOnWriteArraySet<>();
Map<String,String> map = new ConcurrentHashMap<>();

浙公网安备 33010602011771号