CopyOnWrite机制
并发编程从入门到精通学习记录:CopyOnWrite容器通俗地讲,当我们往容器中添加一个元素的时候,不是直接添加,而是对当前容器copy,复制一个容器,在这个复制的容器中添加元素,添加完之后,再将引用指向这个新容器。优点:CopyOnWrite容器可以并发的进行读操作,而不需要加锁,因为 当前容器不会添加任何元素,所以这也是一种读写分离的思想,读和写的操作分开了。缺点:1.内存...
CopyOnWrite容器
通俗地讲,当我们往容器中添加一个元素的时候,不是直接添加,而是对当前容器copy,复制一个容器,在这个复制的容器中添加元素,添加完之后,再将引用指向这个新容器。
优点:CopyOnWrite容器可以并发的进行读操作,而不需要加锁,因为 当前容器不会添加任何元素,所以这也是一种读写分离的思想,读和写的操作分开了。
缺点:
1.内存占用问题,产生了两个容器
2.只能保持数据的最终一致性,无法保持 实时性,所以如果希望读到新数据,不要用copyOnWrite
CopyOnWrite应用场景
用于读多写少的场景,比如白名单,黑名单,搜索中有一些关键字不可以搜索,这些关键字放在一个黑名单中,只有偶尔才更新黑名单
CopyOnWriteArrayList
来看CopyOnWriteArrayList的源码
add方法加锁,如果不加锁,就会copy出很多副本,然后把元素放在新容器,最后把引用指向了新容器
get方法没有锁,简单的从数组中获取元素。但注意,此时如果有其他线程往容器添加元素,它无法获取到新数据,只能获取到旧数据
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
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();
}
}
final void setArray(Object[] a) {
array = a;
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}
}
CopyOnWriteArraySet
set的特点是去重。CopyOnWriteArraySet是用了装饰者模式,内部其实引用了一个CopyOnWriteArrayList,add方法是用了CopyOnWriteArrayList的addIfAbsent方法,如果元素不存在,才加入容器
public class CopyOnWriteArraySet<E> extends AbstractSet<E>
implements java.io.Serializable {
private static final long serialVersionUID = 5457747651344034263L;
private final CopyOnWriteArrayList<E> al;
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
public boolean add(E e) {
return al.addIfAbsent(e);
}
}
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
/**
* A version of addIfAbsent using the strong hint that given
* recent snapshot does not contain e.
*/
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
更多推荐
所有评论(0)