1、简介
1.1 写时复制
写时复制是指:在并发访问的情景下,当需要修改元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改。修改完成之后,将指向原来容器的引用指向新的容器(副本容器)。
1.2 写时复制带来的影响
1 由于不会修改原始容器,只修改副本容器。因此,可以对原始容器进行并发地读。其次,实现了读操作与写操作的分离,读操作发生在原始容器上,写操作发生在副本容器上。
2 数据一致性问题:读操作的线程可能不会立即读取到新修改的数据,因为修改操作发生在副本上。但最终修改操作会完成并更新容器,因此这是最终一致性。
1.2 CopyOnWriteArrayList简介
它相当于线程安全的ArrayList。和ArrayList一样,它是个可变数组;但是和ArrayList不同的时,它具有以下特性:
1. 它最适合于具有以下特征的应用程序:List 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
2. 它是线程安全的。
3. 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
4. 迭代器支持hasNext(), next()等不可变操作,但不支持可变 remove()等操作。
5. 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
1.3 构造函数
public CopyOnWriteArrayList()
public CopyOnWriteArrayList(Collection<? extends E> c)
//将数组toCopyIn的内容赋给CopyOnWriteArrayList
public CopyOnWriteArrayList(E[] toCopyIn)
1.4 API
boolean add(E e)
将指定的元素追加到此列表的末尾。
void add(int index, E element)
在此列表中的指定位置插入指定的元素。
boolean addAll(Collection<? extends E> c)
按照指定集合的迭代器返回的顺序将指定集合中的所有元素追加到此列表的末尾。
boolean addAll(int index, Collection<? extends E> c)
将指定集合中的所有元素插入到此列表中,从指定的位置开始。
int addAllAbsent(Collection<? extends E> c)
将指定集合中尚未包含在此列表中的所有元素按指定集合的迭代器返回的顺序附加到此列表的末尾。
boolean addIfAbsent(E e)
附加元素,如果不存在。
void clear()
从列表中删除所有元素。
Object clone()
返回此列表的浅层副本。
boolean contains(Object o)
如果此列表包含指定的元素,则返回 true 。
boolean containsAll(Collection<?> c)
如果此列表包含指定 true所有元素,则返回true。
boolean equals(Object o)
将指定的对象与此列表进行比较以获得相等性。
void forEach(Consumer<? super E> action)
对 Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
E get(int index)
返回此列表中指定位置的元素。
int hashCode()
返回此列表的哈希码值。
int indexOf(E e, int index)
返回此列表中指定元素的第一次出现的索引,从 index向前 index ,如果未找到该元素,则返回-1。
int indexOf(Object o)
返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
boolean isEmpty()
如果此列表不包含元素,则返回 true 。
Iterator<E> iterator()
以正确的顺序返回该列表中的元素的迭代器。
int lastIndexOf(E e, int index)
返回此列表中指定元素的最后一次出现的索引,从 index ,如果未找到该元素,则返回-1。
int lastIndexOf(Object o)
返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
ListIterator<E> listIterator()
返回列表中的列表迭代器(按适当的顺序)。
ListIterator<E> listIterator(int index)
从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。
E remove(int index)
删除该列表中指定位置的元素。
boolean remove(Object o)
从列表中删除指定元素的第一个出现(如果存在)。
boolean removeAll(Collection<?> c)
从此列表中删除指定集合中包含的所有元素。
boolean removeIf(Predicate<? super E> filter)
删除满足给定谓词的此集合的所有元素。
void replaceAll(UnaryOperator<E> operator)
将该列表的每个元素替换为将该运算符应用于该元素的结果。
boolean retainAll(Collection<?> c)
仅保留此列表中包含在指定集合中的元素。
E set(int index, E element)
用指定的元素替换此列表中指定位置的元素。
int size()
返回此列表中的元素数。
void sort(Comparator<? super E> c)
使用提供的 Comparator对此列表进行排序以比较元素。
Spliterator<E> spliterator()
在此列表中的元素上返回Spliterator 。
List<E> subList(int fromIndex, int toIndex)
返回此列表之间的部分视图 fromIndex ,包容和 toIndex ,排斥。
Object[] toArray()
以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
<T> T[] toArray(T[] a)
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
String toString()
返回此列表的字符串表示形式。
方法与ArrayList类似
2、数据结构
2.1 类继承关系
2.2 源代码
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//同步操作用到的锁对象
final transient ReentrantLock lock = new ReentrantLock();
//实际保存元素的数组 使用volatile修饰
private transient volatile Object[] array;
//
final Object[] getArray() {
return array;
}
//
final void setArray(Object[] a) {
array = a;
}
//默认构造 空数组
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 CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
//查找
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
//设置指定位置的元素
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
//锁同步
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
//如果需要修改
if (oldValue != element) {
int len = elements.length;
//创建一个副本
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
//修改完将原数组丢弃,将副本赋回array对象
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
//添加元素
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;
//将新对象赋给array
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
//移除
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();
}
}
//迭代器
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
//迭代器的修改元素操作会抛UnsupportedOperationException异常
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
@SuppressWarnings("unchecked")
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code remove}
* is not supported by this iterator.
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code set}
* is not supported by this iterator.
*/
public void set(E e) {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code add}
* is not supported by this iterator.
*/
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
Object[] elements = snapshot;
final int size = elements.length;
for (int i = cursor; i < size; i++) {
@SuppressWarnings("unchecked") E e = (E) elements[i];
action.accept(e);
}
cursor = size;
}
}
}
总结:
1 盛放元素的array使用volatile修饰,即有一个线程修改了array,所有的线程都能知道,所以每个线程获取的array都是最新的
2 对元素的增、删、改操作都加锁,然后复制一个副本,对副本操作后将副本的对象赋给array。
这样虽然有线程修改了array,但是不影响其他线程的遍历操作,而且尽量保证了其他线程遍历的array都是最新值。最重要的是对数组进行读操作不需要加锁。
因为每次更改都会复制一个副本,将会占用内存,还会加速垃圾回收,影响性能。元素很多时更甚
所以CopyOnWriteArrayList适合在多线程下遍历操作远大于对元素的修改操作的场景
3 CopyOnWriteArrayList的Iterator不支持add、remove操作,也没有ConcurrentModificationException异常