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()

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