深入学习java并发编程:CopyOnWriteArrayList<E>实现

来源:互联网 发布:orcl 数据库未打开 编辑:程序博客网 时间:2024/05/21 17:10
1、CopyOnWriteArrayList涉及类图

     Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。    
         CopyOnWriteArrayList是一个线程安全、并且在读操作式无锁的ArrayList容器,当我们往其中添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

2、CopyOnWriteArrayList的构造实现以及重要方法 
    transient final ReentrantLock lock = new ReentrantLock();
    private volatile transient Object[] array;
    以上两个是CopyOnWriteArrayList类中两个重要的数据成员, Object[] array是存放列表对象的数组,可以看到使用ReentrantLock来控制并发的对arrayList进行修改。
 1) 构造器
       CopyOnWriteArrayList提供了3个构造器 
//默认的无参数构造器,可以看到这里与ArrayList不同,arrayList创建大小为10的数组,而CopyOnWriteArrayList创建一个大小为0的数组。
 public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
//根据提供的Collection ,来创建CopyOnWriteArrayList
 public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements = c.toArray();
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
        setArray(elements);
    }
//根据已有的泛型数组,创建CopyOnWriteArrayList
 public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }
2)add(E e): boolean  添加元素的方法
//add方法并没有使用synchronized关键字,而是使用了ReentrantLock 来保证线程安全的,不同于ArrayList,这里每次都会创建一个新的Object数组(写时复制),,此数组大小为当前数组大小加1,将之前数组内容复制到新数组中,并将新元素添加到数组末尾,最后将引用赋值给全局数组对象。通过volatile语义保证可见性。
 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();
        }
    }
3)remove(Object o): boolean 移除元素方法
//与add方法一样,此方法也是通过ReentrantLock 锁来保证线程安全,但他和ArrayList所采用的方法不同,首先创建一个比当前数组小1的Object数组,然后移除目标元素,复制其他元素,最后将引用赋值给全局数组对象。
public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = get(elements, index);
            int numMoved = len - index - 1;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }
4)get(int index): E 
//此方法非常简单,直接获取当前数组对应位置的元素,并没有加锁保护,读取的是当时的一个镜像,因此可能出现脏读现象,但是相对而言,读的性能会比较高。
 public E get(int index) {
        return get(getArray(), index);
    }

5)addIfAbsent(E e): boolean 如果不存在,则添加元素方法
public boolean addIfAbsent(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // Copy while checking if already present.
            // This wins in the most common case where it is not present
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = new Object[len + 1];
            for (int i = 0; i < len; ++i) {
                if (eq(e, elements[i]))
                    return false; // exit, throwing away copy
                else
                    newElements[i] = elements[i];
            }
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
6)iterator(): Iterator<E>  获取遍历列表的迭代器方法
// 调用iterator方法后创建一个COWIterator对象实例,保存一个当前数组的快照,在调用next方法遍历时则仅对此快照数据进行遍历,因此遍历
CopyOnWriteArrayList时不会抛出ConcurrentModificationException。
 public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }








0 0
原创粉丝点击