Collections.synchronizedList和CopyOnWriteArrayList在併發場景下都可以使用,Collections.synchronizedList是Collections下的匿名內部類,而CopyOnWriteArrayList是juc包下的。
CopyOnWriteArrayList
推薦使用,juc包下的都是爲併發而生,可以從字面意思就知道CopyOnWriteArrayList是每次寫的時候都會拷貝一份進行操作,是不是就是讀寫分離。
add(E e) 源碼
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; /** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array; /** * Gets the array. Non-private so as to also be accessible * from CopyOnWriteArraySet class. */ final Object[] getArray() { return array; } /** * Sets the array. */ final void setArray(Object[] a) { array = a; } /** * 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(); } }
可以看到使用的ReentrantLock進行加鎖操作,調用getArray獲取當前對象的屬性Object[] array,且使用Arrays.copyOf拷貝一個新的數組,將當前數據加入新數組中,並且重新賦值到array,最後釋放鎖。
如果當前讀操作正好有寫操作,寫的時候是對副本進行操作,而讀的是當前對象,互不影響
/** * {@inheritDoc} * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { return get(getArray(), index); }
CopyOnWriteArrayList就是寫的時候對拷貝對象進行操作,這樣一來讀寫分離保證了List的一致性。
Collections.synchronizedList
Collections中的一個靜態內部類,傳入一個List,它可以將一個非安全的List轉換成一個安全的List,內部使用synchronized對操作進行加鎖。
public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); }
當傳入一個list對象是會進行判斷是否RandomAccess,根據true或false執行不同的構造函數。 有意思的是SynchronizedRandomAccessList繼承於SynchronizedList。
繼承關係
SynchronizedList源碼
/** * @serial include */ 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);} } /** * SynchronizedRandomAccessList instances are serialized as * SynchronizedList instances to allow them to be deserialized * in pre-1.4 JREs (which do not have SynchronizedRandomAccessList). * This method inverts the transformation. As a beneficial * side-effect, it also grafts the RandomAccess marker onto * SynchronizedList instances that were serialized in pre-1.4 JREs. * * Note: Unfortunately, SynchronizedRandomAccessList instances * serialized in 1.4.1 and deserialized in 1.4 will become * SynchronizedList instances, as this method was missing in 1.4. */ private Object readResolve() { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : this); } }
內部使用synchronized來進行加鎖,而鎖的對象mutex就是當前對象this。在其父類SynchronizedCollection中定義的。
/** * @serial include */ 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
值的注意的是在SynchronizedList中存在兩個沒有加鎖的方法,需要用戶手動加鎖的,如下:
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 }
在SynchronizedCollection中也存在沒加鎖的方法,迭代器,分割,流操作都需要手動加鎖!!
/** * @serial include */ static class SynchronizedCollection<E> implements Collection<E>, Serializable { public Iterator<E> iterator() { return c.iterator(); // Must be manually synched by user! } @Override public Spliterator<E> spliterator() { return c.spliterator(); // Must be manually synched by user! } @Override public Stream<E> stream() { return c.stream(); // Must be manually synched by user! } @Override public Stream<E> parallelStream() { return c.parallelStream(); // Must be manually synched by user! } }
在Collections類中,其實就是利用了同步代碼塊來保證數據的安全,而有幾個方法需要手動進行加鎖處理。