CopyOnWriteArrayList定义
ArrayList本身不是线程安全的,在读线程读取ArrayList的数据时,此时在去写入数据,就会触发fast-fail
机制,抛出ConcurrentModificationException
异常。也可以使用Vector
去代替ArrayList
使用,或者使用Collections.synchronizeList()
包裹ArrayList。但他们都是使用synchronized
进行修饰,执行效率不高。
针对运行效率情况,有了CopyOnWriteArrayList
。
适用场景:读多写少。
CopyOnWrite容器
CopyOnWrite
容器即写时复制
的容器。当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行copy,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新的容器。
对CopyOnWrite容器
进行并发读的时候,不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器
也是一种读写分离的思想,读和写采用不同的容器。放弃了数据实时性。
CopyOnWriteArrayList源码解析
重要参数分析
1 2 3 4 5
| final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array;
|
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public CopyOnWriteArrayList() { setArray(new Object[0]); }
public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); }
public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); }
|
插入数据 - add(E e)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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
,保证同一时刻只有一个线程正在进行数组的复制,否则的话内存中会有多份被复制的数据。
volatile
修饰的数组引用,在调用setArray()
时,线程对数组引用的修改是对其他线程可见的。
- 插入数据时插到新的数组中的,可以保证读和写操作在两个数组中执行,不会影响数据。
和ArrayList相比,效率比较低,每次插入一个数组 都需要进行数组复制操作,随着元素的增加,修改代价会越来越大。
获取数据 - get(int index)
1 2 3 4 5 6 7 8
| public E get(int index) { return get(getArray(), index); }
private E get(Object[] a, int index) { return (E) a[index]; }
|
get()
没有添加线程安全控制,也没有加锁。因为get()操作的是旧数组,也不会发生修改操作。
移除数据 - remove(int index)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); } return oldValue; } finally { lock.unlock(); } }
|
拓展
CopyOnWriteArrayList的缺点