java并发编程之美 学习笔记
JUC包中并发List只有CopyOnWriteArrayList
一个。
CopyOnWriteArrayList是一个线程安全的List,对其进行的修改操作都是在底层的一个复制的数组(快照)
上进行的,也就是使用了写时复制策略
。
CopyOnWriteArrayList
属性
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//独占锁对象用来保证同时只有-个线程对array进行修改
final transient ReentrantLock lock = new ReentrantLock();
//array 数组对象用来存放具体元素,只能通过 getArray/setArray方法访问;
private transient volatile Object[] array;
}
构造函数
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[]
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}
add(e)
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取array
Object[] elements = getArray();
int len = elements.length;
//复制array到新数组,新数组长度为array长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//将add的元素e,添加至新数组末尾
newElements[len] = e;
//使用新数组替换老数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
add()
方法,首先要去获取独占锁
,这样就保证只允许一个线程进行add操作(后续更新,删除等操作都是用这一个杜占锁,即同一时刻只允许一个线程进行修改类操作).
在获取杜占锁后,首先复制array到一个新数组,后续的操作都在新数组(快照
)上进行,操作完毕之后,用快照替换array.
remove(index)
public E remove(int index) {
final ReentrantLock lock = this.lock;
//lock
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
//如果index == len-1, 即 len - 1 - index == 0, 说明删除的是最后一个元素
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
//分段copy
//[0,index) , (index , len - 1] ; --- 剔除index
//构建新的array, 长度为len -1 ;
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index); //[0,index)
System.arraycopy(elements, index + 1, newElements, index, numMoved); // (index , len - 1]
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
get(index)
final Object[] getArray() {
return array;
}
private E get(Object[] a, int index) {
return (E) a[index];
}
//get()方法未加锁.
public E get(int index) {
//首先获取array数组,
//如果其他线程将index元素删除了,并不影响get(index)获取,因为删除是在array的一个快照上进行的。
return get(getArray(), index);
}
弱一致性的迭代器
private static void test01(){
final List list = new CopyOnWriteArrayList();
list.add("hello");
list.add("kitty");
//修改程序启动前,获取迭代器
Iterator iterator = list.iterator();
new Thread(()->{
list.remove(1);
}).start();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
打印结果:
为什么没有打印出nihao
或者报错呢?
public Iterator<E> iterator() {
//返回COWiterator对象,此时当前时刻的array
return new COWIterator<E>(getArray(), 0);
}
static final class COWIterator<E> implements ListIterator<E> {
//array的快照
private final Object[] snapshot;
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
//将elements数组作为快照,
snapshot = elements;
}
public boolean hasNext() {
//判断游标 是否小于 快照的长度
return cursor < snapshot.length;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
}
如果在迭代器遍历过程中,其他线程修改了list,那么此时迭代器中的snapshot就是array的快照了,因为在修改的过程中替换了list中的array,此时操作的是另外一个list,
迭代器对list的修改不可见,二者操作的是两个array数组,