List面试题

Vector和ArrayList、LinkedList联系和区别?

  • ArrayList: 底层数组实现;实现RandomAccess接口,支持随机访问;在查询时效率高,非顺序添加元素、删除元素效率低;因为需要复制元素;线程不安全。

  • LinkedList:底层双向链表实现;添加、删除元素快;但查询是遍历查询效率低;线程不安全。

  • Vector:底层数组实现,操作的时候使用synchronized进行加锁,线程安全。
    使用场景

  • Vector弃用

  • ArrayList:频繁读取集合中的元素时;

  • LinkedList:插入和删除操作较多

如果需要保证线程安全,ArrayList应该怎么做,用有几种方式

使用juc并发包下的工具类

  • 使用Collections.synchronizedList(new ArrayList<>()); 原理使用synchronized加锁
  • 使用CopyOnWriteArrayList<>() 原理使用ReentrantLock加锁

Collections.synchronizedList()线程安全的原理

 public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

根据你传入的List是否实现RandomAccess这个接口来返回的SynchronizedRandomAccessList还是SynchronizedList.

ArrayList实现了 RandomAccess 接口,所以返SynchronizedRandomAccessList对象

那就看看SynchronizedRandomAccessList

 static class SynchronizedRandomAccessList<E>
        extends SynchronizedList<E>
        implements RandomAccess {

        SynchronizedRandomAccessList(List<E> list) {
            super(list);
        }

        SynchronizedRandomAccessList(List<E> list, Object mutex) {
            super(list, mutex);
        }

        public List<E> subList(int fromIndex, int toIndex) {
            synchronized (mutex) {
                return new SynchronizedRandomAccessList<>(
                    list.subList(fromIndex, toIndex), mutex);
            }
        }
        private static final long serialVersionUID = 1530674583602358482L;
        private Object writeReplace() {
            return new SynchronizedList<>(list);
        }
    }

SynchronizedRandomAccessList里面没有什么,但它继承了SynchronizedList

我们看看SynchronizedRandomAccessList继承体系

在这里插入图片描述
看看父类SynchronizedList

 static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> {
        private static final long serialVersionUID = -7754090372962971524L;

        final List<E> list;

        SynchronizedList(List<E> list) {
            super(list);
            this.list = list;
        }
        SynchronizedList(List<E> list, Object mutex) {
            super(list, mutex);
            this.list = list;
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return list.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return list.hashCode();}
        }

        public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }
        public E set(int index, E element) {
            synchronized (mutex) {return list.set(index, element);}
        }
        public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

        public int indexOf(Object o) {
            synchronized (mutex) {return list.indexOf(o);}
        }
        public int lastIndexOf(Object o) {
            synchronized (mutex) {return list.lastIndexOf(o);}
        }

        public boolean addAll(int index, Collection<? extends E> c) {
            synchronized (mutex) {return list.addAll(index, c);}
        }

        public ListIterator<E> listIterator() {
            return list.listIterator(); // Must be manually synched by user
        }

        public ListIterator<E> listIterator(int index) {
            return list.listIterator(index); // Must be manually synched by user
        }

        public List<E> subList(int fromIndex, int toIndex) {
            synchronized (mutex) {
                return new SynchronizedList<>(list.subList(fromIndex, toIndex),
                                            mutex);
            }
        }

父类SynchronizedList也是个静态内部类,操作数据都加了synchronized锁,
当我们看SynchronizedList的父类SynchronizedCollection,也能看到SynchronizedCollection类里操作数据也是加了synchronized锁。所以线程安全

CopyOnWriteArrayList<>()线程安全的原理

看看CopyOnWriteArrayList的源码

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

	//ReentrantLock可重入锁 
    final transient ReentrantLock lock = new ReentrantLock();
	
	//volatile 修饰存放数据的数组
    private transient volatile Object[] array;
	.......
	   public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock();
        try {
        	//获取当前数组	
            Object[] elements = getArray();
            //获取当前数组长度
            int len = elements.length;
            // 复制数组到新数组中,并且新数组容量是原来的容量+1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            // 元素赋值给新数组
            newElements[len] = e;
            // 把新数组赋值给当前数组
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

发现CopyOnWriteArrayList操作数据都用了 ReentrantLock加锁。并且通过源码分析,CopyOnWriteArrayList设计思想用了读写分离+最终一致

那么设计也必然也会带来缺点内存占用问题,写时复制机制,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象,如果对象大则容易发生Yong GC和Full GC

CopyOnWriteArrayList<>()与 Collections.synchronizedList()的选择

CopyOnWriteArrayList<>() Collections.synchronizedList()
效率相对高 效率相对低
写(增删改) 效率相对低 效率相对高

读多选CopyOnWriteArrayList<>(),写多选Collections.synchronizedList()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章