//使用Collections.synchronizedList(List list)方法實現線程安全 List<?> list=Collections.synchronizedList(new ArrayList<>());
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
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的讀操做是能夠不用加鎖的。
CopyOnWriteArrayList和Collections.synchronizedList是實現線程安全的列表的兩種方式。兩種實現方式分別針對不一樣狀況有不一樣的性能表現,其中CopyOnWriteArrayList的寫操做性能較差,而多線程的讀操做性能較好。而Collections.synchronizedList的寫操做性能比CopyOnWriteArrayList在多線程操做的狀況下要好不少,而讀操做由於是採用了synchronized關鍵字的方式,其讀操做性能並不如CopyOnWriteArrayList。所以在不一樣的應用場景下,應該選擇不一樣的多線程安全實現類。