很多场景中都是读远远高于写操作,那么每次对读取进行加锁其实是一种资源浪费.根据读写锁的思想,读锁和读锁之间不冲突.
但是读操作会受到写操作的阻碍,在写操作发生时,读就必须等待,否则可能读到不一致的数据.同理读操作正在进行的时候,
程序也不能进行写入.
JDK提供了CopyOnWriteArrayList类,读取是完全不加锁的,并且写入也不会阻塞读取操作,这样一来性能大大提升了.
其实,就是在写操作时,进行一次自我复制,当List需要修改时,并不修改原有内容(这对于保证当前读线程的数据一致性非常重要)
而对原内容进行一次复制,将修改内容写入副本.写完后,再将修改完的副本替换原来的操作.这就保证了写操作不会影响读了.
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array; 原文:https://blog.csdn.net/lan861698789/article/details/81323724 final void setArray(Object[] a) { array = a; }
//关于读取的实现(不上锁): final Object[] getArray() { return array; } private E get(Object[] a, int index) {
return (E) a[index]; }
public E get(int index) { return get(getArray(), index); }
//其他代码 ....... 原文:https://blog.csdn.net/lan861698789/article/details/81323724 //写入操作: 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; //替换老的数组,并且读线程可以立即"察觉"到,因为array变量是volatile类型 setArray(newElements); return true; } finally { lock.unlock(); } } public boolean addAll(Collection<? extends E> c) { Object[] cs = c.toArray(); if (cs.length == 0) return false; final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + cs.length); System.arraycopy(cs, 0, newElements, len, cs.length); setArray(newElements); return true; } finally { lock.unlock(); } } } |
原文:https://blog.csdn.net/lan861698789/article/details/81323724
分析:
和基础arrayList不同:ArrayList每次都会扩容,而copyOnWriteArrayList每次只增加一个or要增加的length长度。
一句话:CopyOnWriteArrayList读不加锁,写才加锁.
而Collections.synchronizedList读写都加锁.