面试系列----集合类线程安全问题

面试系列----集合类线程安全问题

众所周知,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<>();
posted @ 2020-04-03 15:25  ghsticker  阅读(56)  评论(0)    收藏  举报