解决了什么问题
A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on)
引用CopyOnWriteArrayList的doc就是解决了ArrayList并发场景下对容器的修改安全性问题
1 解决方案
涉及修改数组数据或者结构的操作引用副本
2 优点
- 实现简单 空间换时间
- 读多写少场景下几乎不存在因为锁导致的性能瓶颈(极端情况下只读不写则跟ArrayList是一样的)
3 弊端
4 源码
1
| public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
|
实现跟ArrayList几乎一样,只关注List的几个常用api就行。
4.1 CopyOnWriteArrayList#get(int)
没有对入参的脚标显式代码校验,交给jdk底层数组索引IndexOutOfBoundsException
1 2 3 4 5 6 7 8 9 10 11
| public E get(int index) { return this.elementAt(this.getArray(), index); }
static <E> E elementAt(Object[] a, int index) { return (E) a[index]; }
final Object[] getArray() { return this.array; }
|
4.2 CopyOnWriteArrayList#set(int, E)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private transient volatile Object[] array;
public E set(int index, E element) { synchronized (lock) { Object[] es = this.getArray(); E oldValue = this.elementAt(es, index);
if (oldValue != element) { es = es.clone(); es[index] = element; } this.setArray(es); return oldValue; } }
|
4.3 CopyOnWriteArrayList#add(E)
1 2 3 4 5 6 7 8 9 10
| public boolean add(E e) { synchronized (lock) { Object[] es = this.getArray(); int len = es.length; es = Arrays.copyOf(es, len + 1); es[len] = e; setArray(es); return true; } }
|
4.4 CopyOnWriteArrayList#add(int,E)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public void add(int index, E element) { synchronized (lock) { Object[] es = this.getArray(); int len = es.length; if (index > len || index < 0) throw new IndexOutOfBoundsException(outOfBounds(index, len)); Object[] newElements; int numMoved = len - index; if (numMoved == 0) newElements = Arrays.copyOf(es, len + 1); else { newElements = new Object[len + 1]; System.arraycopy(es, 0, newElements, 0, index); System.arraycopy(es, index, newElements, index + 1, numMoved); } newElements[index] = element; this.setArray(newElements); } }
|
4.5 CopyOnWriteArrayList#remove(int)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public E remove(int index) { synchronized (lock) { Object[] es = this.getArray(); int len = es.length; E oldValue = elementAt(es, index); int numMoved = len - index - 1; Object[] newElements; if (numMoved == 0) newElements = Arrays.copyOf(es, len - 1); else { newElements = new Object[len - 1]; System.arraycopy(es, 0, newElements, 0, index); System.arraycopy(es, index + 1, newElements, index, numMoved); } this.setArray(newElements); return oldValue; } }
|
5 总结
- 迭代操作(读)不上锁 不影响性能
- 涉及修改数据或者数据结构的操作(写)(add, set, remove…) => 空间换时间 synchronized{}代码块中的操作就是数据不一致的上限
- 上锁(synchronized)保证线程安全
- 不直接操作数据容器,拷贝一份副本进行操作,结束后改变指针引用
- volatile关键字保证存储数据的数组线程可见性