Java面试题(七) 集合家族之ArrayList在多线程下怎么说才能让面试官满意?

一. ArrayList在多线程下的完全隐患

面试官最喜欢问到的关于ArrayList在多线程下的三问是:
1. ArrayList是线程不安全的,请写一个不安全的例子出来?
2. 怎么解决呢?
3. 更好的解决方案呢?

1. ArrayList是线程不安全的,请写一个不安全的例子出来?

代码演示

public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        //开20个线程就稳稳报错了。
        for (int i = 1; i <= 20; i++) {

            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }

    }

报错结果
在这里插入图片描述
解析

并发修改异常:
java.util.ConcurrentModificationException 

导致异常原因:
     并发争抢修改导致
     例如:一个线程正在写,一个线程过来抢夺,导致数据不一致,导致并发修改异常。

2. 怎么解决呢?

1. List<String> list = new Vector<>();
   使用Vector,因为他是线程安全的,但是这种解决方式很常见,
   没有说到面试官心坎里。
2. List<String> list = Collections.synchronizedList(new ArrayList<>());
	使用集合类中的方法,有点能力,但是还不够。

3. 更好的解决方案呢?

List<String> list = new CopyOnWriteArrayList<>();
写时复制,说出这个面试官才会满意的点点头,赞许的看着你,小伙子不错哦。

4.细说CopyOnWriteArrayList.add方法

CopyOnWrite容器即写时复制,往一个元素添加容器的时候,不直接往当前容器Object[]添加,
而是先将当前容器Object[]进行copy,复制出一个新的容器Object[] newElements,
让后新的容器添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements),
这样做可以对CopyOnWrite容器进行并发的读,而不需要加锁,
因为当前容器不会添加任何元素,
所以CopyOnWrite容器也是一种读写分离的思想,读和写在不同的容器中。

看一下源码就可以很好的理解上面文字的含义了

 /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#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();
        }
    }

二. HashSet与HashMap的解决方案

1.HashSet

HashSet的底层是HashMap:
	但是调用add方法时,添加的是键(key),值是恒定的(Value)虚拟的。

他解决并发修改异常的方式也有二个:

	Set<String> set = Collections.synchronizedSet(new HashSet<>());
	
    Set<String> set = new CopyOnWriteArraySet<>();

2.HashMap

他解决并发修改异常的方式也有二个:

	Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
	
	//这里与前面不一样
    Map<String, String> map = new ConcurrentHashMap<>();
总结:要想打动面试官,就需要对一个问题的多种解决方案都了然于心,需要静下心来好好琢磨,不要浮躁,希望此篇文章对大家有帮助,祝愿各位都能去到自己心仪的公司。

猜你喜欢

转载自blog.csdn.net/w_x_A__l__l/article/details/106658309
今日推荐