自从JDK1.5引入引入concurrentHashmap,CopyOnWriteArraylist等并发集合后,java集合体系得到了很大的完善,我们有多了不少特殊情境的选择。
我们都知道,ArrayList存数数据的结构为数组,获取数据是根据数组下标,因此获取数据速度很快,而插入(中间)数据,删除数据却需要copy数组,因此速度较慢,而相比之下, CopyOnWriteArraylist底层的存储依然是数组,但是基本操作却不大一样。
好处(使用场景):
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。
CopyOnWriteArrayList通过修改时拷贝原数组,而获取数组时返回老数组(相当于快照),使得修改和查看(遍历)的线程操作的是不同数组,达到避免并发修改的问题
缺点(不宜使用):
1.内存占用问题。
因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存)。如果这些对象占用的内存比较大,比如说200M左右,那么再写入100M数据进去,内存就会占用300M,那么这个时候很有可能造成频繁的Yong GC和Full GC。之前我们系统中使用了一个服务由于每晚使用CopyOnWrite机制更新大对象,造成了每晚15秒的Full GC,应用响应时间也随之变长。
针对内存占用问题,可以通过压缩容器中的元素的方法来减少大对象的内存消耗,比如,如果元素全是10进制的数字,可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器,而使用其他的并发容器,如ConcurrentHashMap。
2.数据一致性问题。
CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
3.性能。
每次写都要复制的操作自然造成操作性能的下降。
代码:
添加
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
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();
}
}
通过Arrays的copyOf方式复制数组,同时,写的操作是需要并发控制的,采用了显示锁
写的操作直接对新的数组执行
获取数组
final Object[] getArray() {
return array;
}
来源:oschina
链接:https://my.oschina.net/u/2317039/blog/403431