//使用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。所以在不一样的应用场景下,应该选择不一样的多线程安全实现类。