java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > CopyOnWriteArrayList详解

Java中的拷贝数组CopyOnWriteArrayList详解

作者:Brain_L

这篇文章主要介绍了Java中的拷贝数组CopyOnWriteArrayList详解,ArrayList和LinkedList都不是线程安全的,如果需要线程安全的List,可以使用synchronizedList来生成一个同步list,但是这个同步list的方法都是通过synchronized修饰来保证同步的,需要的朋友可以参考下

CopyOnWriteArrayList详解

ArrayList和LinkedList都不是线程安全的,如果需要线程安全的List,可以使用Collections.synchronizedList来生成一个同步list,但是这个同步list的方法都是通过synchronized修饰来保证同步的,并发性能不高。那么如何提高并发性能呢?比如某些场景下,对List的读操作远多于写操作,那么CopyOnWriteArrayList就派上用场了。

1、属性

/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

lock是用来在写操作时加锁使用的,具体使用下面方法部分再看。

数组array是CopyOnWriteArrayList的核心部分了,所有的元素都存放在这个数组中。为了保证可见性,所以被volatile修饰着。

2、方法

final Object[] getArray() {
    return array;
}
final void setArray(Object[] a) {
    array = a;
}
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        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);
}
public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

三个构造函数都是为了给array数组赋值,生成初始数组。

下面看下它的几个关键方法:get、add、remove

private E get(Object[] a, int index) {
    return (E) a[index];
}
/**
 * {@inheritDoc}
 *
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    return get(getArray(), index);
}

get比较简单,就是直接取数组索引处的元素。注意get方法没有加锁。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    //1、加锁
    lock.lock();
    try {
        //2、取原数组
        Object[] elements = getArray();
        int len = elements.length;
        //3、拷贝生成新数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //4、新元素加到数组最后一位
        newElements[len] = e;
        //5、数组替换
        setArray(newElements);
        return true;
    } finally {
        //6、释放锁
        lock.unlock();
    }
}

add方法,上来就先加锁,然后取出原数组后拷贝生成了一个新的数组,注意,此时原有的array数组没有变,get访问时还是跟之前一样。当把新的数组替换掉array后,由于是volatile修饰的,get访问时就会访问添加过元素的新数组。这样就保证了读写同时进行时,读不需要加锁依然不会有并发问题。最后释放锁后,别的写操作获得锁,再次进行替换操作,这样保证写操作与写操作之间不会有并发问题。

public E remove(int index) {
    final ReentrantLock lock = this.lock;
    //1、获取锁
    lock.lock();
    try {
        //2、取原数组
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = get(elements, index);
        int numMoved = len - index - 1;
        //3、如果被删除元素是数组最后一位,直接截取len-1的新数组
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        //4、否则,分段拷贝生成新数组
        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 {
        //5、释放锁
        lock.unlock();
    }
}

remove与add同理,也是先获取锁,同时生成新的数组之后再把原有的数组进行替换。

3、总结

到此这篇关于Java中的拷贝数组CopyOnWriteArrayList详解的文章就介绍到这了,更多相关CopyOnWriteArrayList详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文