CopyOnWriteArrayList学习

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

之前一直没用过CopyOnWriteArrayList,只知道给ArrayList,直到多线程编程才发现ArrayList是线程非安全的,后来就了解到了CopyOnWriteArrayList;

使用ArrayList出现的问题

ArrayList<String> list=new ArrayList<String>();
//        CopyOnWriteArrayList list=
        list.add("a");
        list.add("b");
        list.add("c");
//        final ArrayList<String> testlist=new ArrayList<>(list);出错,ConcurrentModificationException错误
        final CopyOnWriteArrayList<String> testlist=new CopyOnWriteArrayList<>(list);
        Thread thread = new Thread(new Runnable() {

            int count = -1;

            @Override
            public void run() {

                while(true){

                    testlist.add(count++ + "");
                }
            }
        });
        thread.setDaemon(true); //在启动了线程之后随着main方法执行完毕而终止,否则一直停在while循环
        thread.start();
        Thread.currentThread().sleep(3);
        for(String s: testlist){

            System.out.println(s);
            System.out.println(testlist.hashCode());
        }

换成CopyOnWriteArrayList后,就没有出现问题,然后就了解这个代码的内幕是什么样的,简单粗暴的原因就是去查看源码

  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;
    }

add方法中,使用了锁,然后就数组copy一份,将原来的数组引用指向新的数组

有优点就有缺点,缺点大概有这几方面

1.内存占用问题,因为要进行数组的复制,这样就多了一份拷贝在内存中,如果数据比较大的话,比如100M,复制就会成为200M,这样有可能会造成频繁的yongGC和fullGC;

2.实时一致性,CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。

根据CopyOnWrite的实现机制,可以去实现CopyOnWriteMap,CopyOnWriteSet等等;这类主要应用场景为多读少写的并发情况,比如我们针对网站设置一个IP过滤器,每个IP进来的时候先查询是否在过滤器里面,如果存在就阻断访问,然后每天再将当天访问超过10000次的IP加入到过滤器里面。

原文地址:https://www.cnblogs.com/dpains/p/7133622.html