—–CopyOnWrite容器即写时复制的容器。通俗的讲就是往容器里面写数据的时候,不再当前容器写数据,而是将当前的容器拷贝一份,往拷贝新的容器里面写数据,添加完元素后,再将原容器指向新的容器。这样做的好处就是,并发读的时候不需要加锁,因为当前容器没有添加任何元素,不过写的时候需要加锁,所以CopyOnWrite也是一种读写分离的思想,适用于读多写少的并发场景,CopyOnWriteArrayList采用“写入时复制”策略,对容器的写操作将导致的容器中基本数组的复制,性能开销较大。所以在有写操作的情况下,CopyOnWriteArrayList性能不佳,而且如果容器容量较大的话容易造成溢出。
public class Test1 {
final static CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList();
final static AtomicInteger atomicInteger = new AtomicInteger(3);
public static void main(String[] args) {
List<String> lists = Lists.newArrayList();
lists.add("1");
lists.add("2");
//整体加入会更好,单个加入需要消耗大量性能
copyOnWriteArrayList.addAll(lists);
product();
comsumer();
try {
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static void product() {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
TimeUnit.NANOSECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
copyOnWriteArrayList.add(atomicInteger.incrementAndGet()+"");
}).start();
}
}
static void comsumer() {
//读多写少
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
System.out.println(copyOnWriteArrayList.toString());
}).start();
}
}
}
CopyOnWrite的缺点
内存占用的问题:CopyOnWrite写的复制机制,导致在写的时候内存存在两份对象(注意:在复制的时候只是复制容器对象的引用,只是在写的时候会在创建一个新对象添加到新的容器里,而旧的容器还在使用)。如果这些对象比较大,那么这个时候很可能造成频繁Yong GC和Full GC,针对这种大对象内存占用问题,可以通过压缩容器的元素来减少大对象的内存消耗,比如10进制变成36进制。或者不使用CopyOnWrite,而使用其它并发容器,ConcurrentHashMap。
数据一致性问题:由于采用了读写分离的思想,所以读和写可能获取的数据是不一样的,它只能保证数据的最终一致性,不能保证数据的实时一致性。