Java集合源码解析 - CopyOnWriteArrayList

概述

CopyOnWriteArrayList是一个以数组和可重入锁实现的List(线程安全),每次对数组的修改时都先拷贝一份新的数组来操作,修改完成后在覆盖旧数组,保证只阻塞写操作,不阻塞读操作,实现读写分离

UML类图

CopyOnWriteArrayList UML
CopyOnWriteArrayList实现了List, RandomAccess, Cloneable, java.io.Serializable等接口;
CopyOnWriteArrayList实现了List,提供了基础的添加、删除、遍历等操作;
CopyOnWriteArrayList实现了RandomAccess,提供了随机访问的能力;
CopyOnWriteArrayList实现了Cloneable,可以被克隆;
CopyOnWriteArrayList实现了Serializable,可以被序列化;

源码解析

属性

/** 可重入锁,写操作时应用 */
final transient ReentrantLock lock = new ReentrantLock();
/** volatile修饰数组,保证变量内存可见性. */
private transient volatile Object[] array;

构造器

/**
 * 创建空数组.
 */
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

/** param:collection    */
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));
}

/** 取值. */
final Object[] getArray() {
    return array;
}

/**
 * 设值.
 */
final void setArray(Object[] a) {
    array = a;
}

方法

add(E e)

功能

添加元素到数组末尾,平均时间复杂度O(n);

流程
  1. 加锁
  2. 拷贝新数组
  3. 末尾加元素
  4. 覆盖旧数组
  5. 解锁
源码
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();
    }
}

add(int index, E e)

功能

添加元素到数组指定位置,平均时间复杂度O(n);

流程
  1. 加锁
  2. 数组越界判断
  3. 分段拷贝数据到新数组并在指定节点加入元素
  4. 覆盖旧数组
  5. 解锁
源码
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();
    }
}

get(int index)

功能

根据指定下标返回数据,平均时间复杂度O(1);

源码
public E get(int index) {
    return get(getArray(), index);
}

private E get(Object[] a, int index) {
    return (E) a[index];
}

remove(int index)

功能

功能:删除并返回指定下标的数据,平均时间复杂度为O(n)

流程
  1. 加锁
  2. 获取旧数组并删除指定元素
  3. 分段拷贝数据至新数组并清除最后一位元素
  4. 解锁
源码
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)
        //如果要删除的元素刚好是最后一个,拷贝新数组-1个元素会旧数组
            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();
    }
}

总结

CopyOnWriteArrayList使用ReentrantLock重入锁,保证线程安全
CopyOnWriteArrayList写操作都要先拷贝一份新数组,在新数组中修改完覆盖旧数组,平均时间复杂度O(n)
CopyOnWriteArrayList支持随机访问,平均时间复杂度O(1)
CopyOnWriteArrayList采用读写分离的思想,写时加锁,读时不加锁,适合读多写少的场景
CopyOnWriteArrayList可保证最终结果一致性,不保证实时一致性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值