CopyOnWriteArrayList实现原理-写时复制
CopyOnWriteArrayList 写时复制器
CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet。
实现原理
我们都知道,集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但由于简单粗暴的锁同步机制,性能较差。而CopyOnWriteArrayList则提供了另一种不同的并发处理策略(当然是针对特定的并发场景)。
很多时候,我们的系统应对的都是读多写少的并发场景。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。
优点:
读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。
Java的list在遍历时,若中途有别的线程对list容器进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的list容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了。
缺点:
缺点也很明显:
一是内存占用问题,毕竟每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC;
二是无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性。而CopyOnWriteArrayList由于其实现策略的原因,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据。
如下这段代码:
在子线程中,不断向CopyOnWriteArrayList<String>中添加元素。
主线程则在遍历 list对象。因为子线程中list 中不断添加新元素,程序会一直运行。
package com.demo.concurrent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by rickie on 2018/12/12.
*/
public class CopyOnWriteDemo {
public static void main(String[] args) throws InterruptedException {
//List<String> lst = new ArrayList<String>();
CopyOnWriteArrayList<String> lst = new CopyOnWriteArrayList<String>();
lst.add("a");
lst.add("b");
lst.add("c");
//final CopyOnWriteArrayList<String> cow = new CopyOnWriteArrayList<String>(lst);
//final ArrayList<String> cow = new ArrayList<>(lst);
Thread t = new Thread(new Runnable() {
int count = -1;
@Override
public void run() {
while (true){
lst.add(count++ + "");
System.out.println(count);
}
}
});
t.setDaemon(true);
t.start();
Thread.currentThread().sleep(300);
for(String s: lst){
System.out.println(lst.hashCode() + " -- " + s);
}
}
}