属性:
在JDK11中,将reentrantLock改为关键字synchronized
//锁
final transient Object lock = new Object();
//数组,保存数据
private transient volatile Object[] array;
在CopyOnWriteArrayList中不需要想ArrayList一样记录size属性,因为数组长度就是集合中元素的个数。
构造函数:
//无参构造函数,创建空数组
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] es;
//是CopyOnWriteArrayList类型,那么直接把它的数组拿过来使用
if (c.getClass() == CopyOnWriteArrayList.class)
es = ((CopyOnWriteArrayList<?>)c).getArray();//浅拷贝
else {
es = c.toArray();
if (es.getClass() != Object[].class)
es = Arrays.copyOf(es, es.length, Object[].class);
}
setArray(es);
}
add方法
public boolean add(E e) {
//写操作加锁
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
//将旧数组拷贝到新数组
es = Arrays.copyOf(es, len + 1);
//元素加入新数组末尾
es[len] = e;
//重置数组
setArray(es);
return true;
}
}
总结:
1.获取锁
2.将旧数组拷贝到新数组中,新数组长度加一
3.元素加入新数组末尾
4.重置array属性
6.隐式释放锁
get方法
public E get(int index) {
return elementAt(getArray(), index);
}
static <E> E elementAt(Object[] a, int index) {
return (E) a[index];
}
直接返回数组元素,不需要加锁
remove方法
public E remove(int index) {
//获取锁
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
E oldValue = elementAt(es, index);
int numMoved = len - index - 1;
Object[] newElements;
//如果删除最后一位,直接拷贝前len-1位
if (numMoved == 0)
newElements = Arrays.copyOf(es, len - 1);
//如果删除中间元素
else {
//新建数组,长度位len-1
newElements = new Object[len - 1];
//拷贝index以前的元素
System.arraycopy(es, 0, newElements, 0, index);
//拷贝index以后的元素
System.arraycopy(es, index + 1, newElements, index,
numMoved);
}
//重置array
setArray(newElements);
return oldValue;
}
}
remove方法与add方法类似
总结:
1.CopyOnWriteArrayList使用synchronized关键字保证线程安全
2.CopyOnWriteArrayList的写操作需要加锁,读操作不需要加锁
3.CopyOnWriteArrayList写操作时需要拷贝一份新的数组,在新数组中修改
4.由2和3可知,CopyOnWriteArrayList适合读多写少的并发环境
5.CopyOnWriteArrayList具有弱一致性