CopyOnWriteArrayList提供线程安全性和可伸缩性
可伸缩性指的是一个应用程序在工作负载和可用处理资源增加时其吞吐量的表现情况。
一个可伸缩的程序能够通过使用更多的处理器、内存或者I/O带宽来相应地处理更大的工作负载。
锁住某个共享的资源以获得独占式的访问这种做法会形成可伸缩性瓶颈――它使其他线程不能访问那个资源,即使有空闲的处理器可以调用那些线程也无济于事**。为了取得可伸缩性,我们必须消除或者减少我们对独占式资源锁的依赖。**
CopyOnWriteArrayList是java1.5版本提供的一个线程安全的ArrayList变体,ArrayList具有fast-fail特性,它是值在遍历过程中,如果ArrayList的内容发生过修改,那么会抛出ConcurrentModificationException。
在多线程环境下,这种情况变得尤为突出。不使用迭代器形式而使用下标来遍历就会带来一个问题,读写没有分离。写操作户影响到读的准确性,甚至导致IndexOutOfBoundsException。不直接遍历list,而是把list拷贝一份数组,再行遍历。
写时拷贝,自然实在做写操作时,把原始数据拷贝到一个新的数组,与写操作相关的主要有三个方法:add、remove、set,在每一次add操作里,数组都被拷贝了一个副本,这就是写时拷贝的原理,那么写时拷贝和读时拷贝各有什么优势,如何选择呢?
如果一个list的遍历操作比写入操作更频繁,那么用该使用CopyOnWriteArrayList,如果list的写入操作比遍历操作更频繁,那么应该考虑读时拷贝。
CopyOnWriteArrayList
源码原理分析CopyOnWrite
则做了一个升级:读取是完全不加锁的,并且写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待。(读写不互斥,写写互斥)/** * CopyOnWriteArrayList可以在迭代中修改数组内容,而ArrayList不行 * @author yiren */ public class CopyOnWriteArrayListExample01 { public static void main(String[] args) { // ArrayList<String> list = new ArrayList<>(); CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(list); String next = iterator.next(); System.out.println(next); if (next.equals("2")) { list.remove("3"); } if (next.equals("4")) { list.add("3 add"); } } } } [1, 2, 3, 4, 5] 1 [1, 2, 3, 4, 5] 2 [1, 2, 4, 5] 3 [1, 2, 4, 5] 4 [1, 2, 4, 5, 3 add] 5 Process finished with exit code 0
结果输出和list中的元素不对应。CopyOnWriteArrayList是这个思想,迭代你可以改,但是你改你的,我迭代我的,它内部是副本机制,这个和ArrayList的迭代器不一样,ArrList里面有一个modCount值来判断你迭代过程中是否修改的
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
/** * @author yiren */ public class CopyOnWriteArrayListExample02 { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); list.add("1"); list.add("2"); list.add("3"); Iterator<String> itr1 = list.iterator(); list.add("4"); Iterator<String> itr2 = list.iterator(); itr1.forEachRemaining(System.out::print); System.out.println(); itrforEachRemaining(System.out::print); } } 123 1234 Process finished with exit code 0
private transient volatile Object[] array;
/** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock();
public CopyOnWriteArrayList() { setArray(new Object[0]); }
public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); }
public boolean add(E e) { final ReentrantLock lock = this.lock; //添加的时候先上锁 lock.lock(); try { // copy一份到新数组,数组长度+1 Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); // 新值放到末尾,把指针指过去 newElements[len] = e; // 最后返回true 并释放锁 setArray(newElements); return true; } finally { lock.unlock(); } }
@SuppressWarnings("unchecked") private E get(Object[] a, int index) { return (E) a[index]; } /** * {@inheritDoc} * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { return get(getArray(), index); }