ArrayList的實現原理以及實現線程安全

1、ArrayList概述

  1. ArrayList是基於數組實現的,是一個動態的數字,能夠自動擴容。
  2. ArrayList不是線程安全的,效率比較高,只能用於單線程的環境中,在多線程環境中可使用Collections.synchronizedList(List list)函數返回一個線程安全的ArrayList集合,或者使用concurrent併發包下的CopyOnWriteArrayList的。
//使用Collections.synchronizedList(List list)方法實現線程安全
List<?> list=Collections.synchronizedList(new ArrayList<>());

2、經過源碼解析Collections.synchronizedList(List list)方法如何實現線程安全?

public static List synchronizedList(List list1) {
	return ((List) ((list1 instanceof RandomAccess) ? new SynchronizedRandomAccessList(list1)
				: new SynchronizedList(list1)));
}

         經過判斷傳入的list類是否爲RandomAccess類,若是是則實例化SynchronizedRandomAccessList,若是不是,則實例化SynchronizedList。若是你傳入的list集合是ArrayList或者Vector。那麼則是實例化SynchronizedRandomAccessList。咱們從源碼能夠查看緣由:java

ArrayList集合源碼數組

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable {

Vector集合源碼:安全

public class Vector extends AbstractList implements List, RandomAccess, Cloneable, Serializable {

        咱們能夠發現ArrayList和Vector集合二者都實現了List和RandomAccess接口。接下來看看SynchronizedRandomAccessList類的實現:多線程

static class SynchronizedRandomAccessList extends SynchronizedList implements RandomAccess {

		public List subList(int i, int j)
        {
            Object obj = mutex;
            return new SynchronizedRandomAccessList(list.subList(i, j), mutex);
        }

		private Object writeReplace() {
			return new SynchronizedList(list);
		}

		private static final long serialVersionUID = 1530674583602358482L;

		SynchronizedRandomAccessList(List list1) {
			super(list1);
		}

		SynchronizedRandomAccessList(List list1, Object obj) {
			super(list1, obj);
		}
	}

       由於SynchronizedRandomAccessList這個類繼承自SynchronizedList,而大部分方法都在SynchronizedList中實現了,因此源碼中只包含了不多的方法。咱們來看看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);
        }
    }

    @Override
    public void replaceAll(UnaryOperator<E> operator) {
        synchronized (mutex) {list.replaceAll(operator);}
    }
    @Override
    public void sort(Comparator<? super E> c) {
        synchronized (mutex) {list.sort(c);}
    }
    ... ...
}

       能夠發現SynchronizedList方法中地方都用到了同步鎖,而且參數都是mutex。咱們經過點擊mutex字段進入到SynchronizedCollection類中,能夠得知mutex是在SynchronizedCollection類中定義的。咱們繼續查看SynchronizedCollection部分源碼:dom

static class SynchronizedCollection<E> implements Collection<E>, Serializable {  
    private static final long serialVersionUID = 3053995032091335093L;  
    final Collection<E> c;  // Backing Collection  
    final Object mutex;     // Object on which to synchronize  

    SynchronizedCollection(Collection<E> c) {  
            if (c==null)  
                throw new NullPointerException();  
        this.c = c;  
            mutex = this;  
        }  

    SynchronizedCollection(Collection<E> c, Object mutex) {  
        this.c = c;  
            this.mutex = mutex;  
        }  
}

        能夠發現其實咱們調用synchronizedList方法的使用,內部鎖都是同樣的,因此它能夠實現線程的同步。ide

3、經過源碼解析CopyOnWriteArrayList如何作到線程安全的?

CopyOnWriteArrayList使用了一種叫寫時複製的方法,當有新元素添加到CopyOnWriteArrayList時,先從原有的數組中拷貝一份出來,而後在新的數組作寫操做,寫完以後,再將原來的數組引用指向到新數組。函數

當有新元素加入的時候,以下圖,建立新數組,並往新數組中加入一個新元素,這個時候,array這個引用仍然是指向原數組的。性能

當元素在新數組添加成功後,將array這個引用指向新數組。this

CopyOnWriteArrayList的整個add操做都是在鎖的保護下進行的。 
這樣作是爲了不在多線程併發add的時候,複製出多個副本出來,把數據搞亂了,致使最終的數組數據不是咱們指望的。咱們來看看
CopyOnWriteArrayList源碼:

transient final ReentrantLock lock = new ReentrantLock();  
  
    /** The array, accessed only via getArray/setArray. */  
    private volatile transient Object[] array;//保證了線程的可見性  
      
    public boolean add(E e) {  
        final ReentrantLock lock = this.lock;//ReentrantLock 保證了線程的可見性和順序性,即保證了多線程安全。  
        //一、先加鎖
        lock.lock();  
        try {  
           Object[] elements = getArray();  
           int len = elements.length;  
           Object[] newElements = Arrays.copyOf(elements, len + 1);////二、拷貝數組,在原先數組基礎之上新建長度+1的數組,並將原先數組當中的內容拷貝到新數組當中。 
           
           //三、將元素加入到新數組中
           newElements[len] = e;
           //四、將array引用指向到新數組
           setArray(newElements);//對新數組進行賦值  
           return true;  
        } finally {  
           lock.unlock();  
        }  
    }

因爲全部的寫操做都是在新數組進行的,這個時候若是有線程併發的寫,則經過鎖來控制,若是有線程併發的讀,則分幾種狀況: 
一、若是寫操做未完成,那麼直接讀取原數組的數據; 
二、若是寫操做完成,可是引用還未指向新數組,那麼也是讀取原數組數據; 
三、若是寫操做完成,而且引用已經指向了新的數組,那麼直接重新數組中讀取數據。

可見,CopyOnWriteArrayList的讀操做是能夠不用加鎖的。

4、Collections.synchronizedList & CopyOnWriteArrayList

       CopyOnWriteArrayList和Collections.synchronizedList是實現線程安全的列表的兩種方式。兩種實現方式分別針對不一樣狀況有不一樣的性能表現,其中CopyOnWriteArrayList的寫操做性能較差,而多線程的讀操做性能較好。而Collections.synchronizedList的寫操做性能比CopyOnWriteArrayList在多線程操做的狀況下要好不少,而讀操做由於是採用了synchronized關鍵字的方式,其讀操做性能並不如CopyOnWriteArrayList。所以在不一樣的應用場景下,應該選擇不一樣的多線程安全實現類。

相關文章
相關標籤/搜索