CopyOnWriteArrayList-源码分析

来源:互联网 发布:淘宝开店 平板 编辑:程序博客网 时间:2024/05/19 03:29
类结构
public class CopyOnWriteArrayList<E>extends Objectimplements List<E>, RandomAccess, Cloneable, Serializable

类说明
A thread-safe variant ofArrayList in which all mutative operations (add,set, and so on) are implemented by making a fresh copy of the underlying array.
这个类通过生成底层数组的新副本来实现线程安全,是ArrayLis的变体实现

This is ordinarily too costly, but may bemore efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads.
这通常代价太高,但当遍历操作远远超过变更时,可能比其他选择更有效,当你不能或不想同步遍历,
却需要排除并发线程间的干扰,这时候(这个类的这个操作)就很有用

The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created.
当快照类型的迭代器创建的那一刻起,就使用了数组的引用

This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throwConcurrentModificationException.
这个数组在迭代器的生命周期内绝不会改变,不会有干扰,也不会抛出ConcurrentModificationException异常,因此可以放心的使用

The iterator will not reflect additions, removals, or changes to the list since the iterator was created.
这个迭代器自创建之后,就不会反应在list中的添加、删除和更改中

Element-changing operations on iterators themselves (remove,set, and add) are not supported.
These methods throwUnsupportedOperationException.
在迭代器中的元素是不可以更改的,如果进行类似remove,set,add的更改操作就会报异常UnsupportedOperationException

All elements are permitted, includingnull.
元素类型可以允许所有类型,包括null

Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into aCopyOnWriteArrayList happen-before actions subsequent to the access or removal of that element from theCopyOnWriteArrayList in another thread.
内存一致性效果:和其他并发集合一样,如果线程在删除或访问元素的时候有线程添加元素,那么添加元素的操作会优先执行

构造方法
CopyOnWriteArrayList()//常规创建方法,创建一个空的list,默认大小是0
CopyOnWriteArrayList(Collection<? extends E> c)
CopyOnWriteArrayList(E[] toCopyIn)

常用方法(增、查、改、删)分析
//直接把元素添加的list的末尾
    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();        }    }



//添加到指定位置
    public void add(int index, E element) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            Object[] elements = getArray();            int len = elements.length;            if (index > len || index < 0)                throw new IndexOutOfBoundsException("Index: "+index+                                                    ", Size: "+len);            Object[] newElements;            int numMoved = len - index;            if (numMoved == 0)                newElements = Arrays.copyOf(elements, len + 1);            else {                newElements = new Object[len + 1];//最耗时的地方,由此可以看出,越往中间添加,耗时越久                System.arraycopy(elements, 0, newElements, 0, index);                System.arraycopy(elements, index, newElements, index + 1,                                 numMoved);            }            newElements[index] = element;            setArray(newElements);        } finally {            lock.unlock();        }    }


//判断是否存在,越往后,耗时越久
public boolean contains(Object o) {
Object[] elements = getArray();
return indexOf(o, elements, 0, elements.length) >= 0;
}

//越往后,查找越久
    private static int indexOf(Object o, Object[] elements,                               int index, int fence) {        if (o == null) {            for (int i = index; i < fence; i++)                if (elements[i] == null)                    return i;        } else {            for (int i = index; i < fence; i++)                if (o.equals(elements[i]))                    return i;        }        return -1;    }


//list越大,耗时越久
    public E set(int index, E element) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            Object[] elements = getArray();            E oldValue = get(elements, index);            if (oldValue != element) {                int len = elements.length;//理论上,list越大,耗时越久                Object[] newElements = Arrays.copyOf(elements, len);                newElements[index] = element;                setArray(newElements);            } else {                // Not quite a no-op; ensures volatile write semantics                setArray(elements);            }            return oldValue;        } finally {            lock.unlock();        }    }


//删除的时候越往中间,耗时越久
    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();        }    }

//同理,
public boolean remove(Object o) {
Object[] snapshot = getArray();
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
}
//这里需要再看一次
    private boolean remove(Object o, Object[] snapshot, int index) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            Object[] current = getArray();            int len = current.length;            if (snapshot != current) findIndex: {                int prefix = Math.min(index, len);                for (int i = 0; i < prefix; i++) {                    if (current[i] != snapshot[i] && eq(o, current[i])) {                        index = i;                        break findIndex;                    }                }                if (index >= len)                    return false;                if (current[index] == o)                    break findIndex;                index = indexOf(o, current, index, len);                if (index < 0)                    return false;            }            Object[] newElements = new Object[len - 1];            System.arraycopy(current, 0, newElements, 0, index);            System.arraycopy(current, index + 1,                             newElements, index,                             len - index - 1);            setArray(newElements);            return true;        } finally {            lock.unlock();        }    }


总结
CopyOnWriteArrayList跟ArrayList对比
1、前者消耗内存较多,因为每一个操作,都有一个数据备份,后者相对较少
2、前者是线程安全,后者不是
3、当数据越来越多的时候,前者的所有操作都相对较慢,而后者相对较快
4、前者进行所有操作的时候,都是操作数据的一个副本,而后者是直接操作原数据上
5、两者的底层实现都是数组

1、以上源码分析中为什么会没有获取元素的方法?(get(E e))
原因是,这个get方法太特殊了,设计的思路很奇特,不需要什么特别分析,不过,如果需要跟Vector来比较的话,那么就有需要来看看这个方法,
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
final Object[] getArray() {
return array;
}
仅仅用三个方法完美搞掂,神奇得不太敢想
回头看看Vector的get方法
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);

return elementData(index);
}
其实也没有什么特殊,只不过多了一个修饰的关键字synchronized,这个关键字有重要的影响,这个关键字的作用大家都知道,就是同步关键字。
看完所有,现在出结论
CopyOnWriteArrayList和 Vector比较
1、两者都是线程安全,就是有同步的
2、两者的同步实现是不同方式,前者是用Lock,后者是用synchronized关键字
3、两者的底层数据结构都是数组,
4、两者修改数据的方法都有同步,
5、前者获取数据没有同步,后者获取数据有同步

在都需要同步的情况下,
1、多读少写,用CopyOnWriteArrayList(看前面获取数据的结论第5点)
2、多写少度,用Vector


原创粉丝点击