数据结构-CopyOnWriteList
` CopyOnWriteList 是线程安全的List版本之一,CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。
文章目录
前言
`
提示:以下是本篇文章正文内容,下面案例可供参考
一、类的关系及主要属性
二、具体属性及方法解释
1.array
volatile 修饰,保证每次拿到都是最新值,volatile表示直接从主存中获取,别的线程如果更新了 arrary,也会将array 更新到主存中。
private transient volatile Object[] array;
2.get(index)
直接返回数组中的对象
public E get(int index) {
return elementAt(getArray(), index);
}
3.set(int index,Element element)
大致流程
- 用synchronized 加锁,
- 获取原数组中index的valve (oldValue)
- 判断oldValue 与element 是否相等
- 如果不等,则clone原数组。并更新index的值为element,然后更新clone数组
如果相等则返回 oldeValue (oldValue=element)
public E set(int index, E element) {
synchronized (lock) {
Object[] es = getArray();
E oldValue = elementAt(es, index);
if (oldValue != element) {
es = es.clone();
es[index] = element;
setArray(es);
}
return oldValue;
}
}
4.add(E e)
大致流程
- 用synchronized 枷锁
- getArray() 获取原始数组 es
- 获取es 的长度,并将es 拷贝到 es.length+1 到新数组中,并将array=新数组
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.更新数组为何要copy 一个新数组?
别忘了读操作,读操作是没有加锁的。为了读的高效率。读操作直接在原始数组上读取。不用等新数组更新完 再读区。所以CopyOnWrite适合读多写少的情况。
2.更新操作为何要使用synchronized?
防止其他写线程的影响。所以说如果是读少写多的情况,就是不适合使用CopyONWirte*,因为写会加锁,性能不好。
3.CopyOnWrite的缺点
1.内存占用大
每次add()、set()、remove()这些增删改操作都要复制一个数组出来。
2.数据一致性弱
CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。
比如 thread_0调用get 的同时,thread_1 正在set(), thread_0读到是旧值。
再比如 迭代原始时,别的线程调用了set,remove,add,也只会遍历旧值。