JDK1.8源码解析-CopyOnWriteArrayList

类关系图

 

属性

因为CopyOnWriteArrayList最主要的功能是实现写时复制,而实现写时复制的关键就是要在实现在并发情况下数据的遍历与修改能够同步进行,这就需要通过可重入锁实现修改时的数组同步。

/** 保护所有存取器的锁 */
final transient ReentrantLock lock = new ReentrantLock();

/** 只能通过 getArray/setArray访问的数组. */
private transient volatile Object[] array;

/**
 * 获取数组,设为非私有为了也能在 CopyOnWriteArraySet 中使用.
 */
final Object[] getArray() {
    return array;
}

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


构造方法

这个构造方法与arraylist的构造方法几乎相同。在创建含有特定集合的数组时,都需要通过调用copyOf方法进行数组复制,当然首先要进行的是类型的判断,若类型相同则直接调用toArray方法即可。

public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

/**
 *创建一个包含指定集合元素的列表。 
 */
public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    if (c.getClass() == CopyOnWriteArrayList.class)//若集合类型是CopyOnWriteArrayList,直接获取数组进行赋值
        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));
}

方法

返回指定元素下标-indexOf

/**
 * indexOf的静态版本, 允许重复调用且不需要每次重新获取数组
 * @param o 要查找的元素
 * @param elements 数组
 * @param index 搜索的起始位置
 * @param fence 搜索的最后一个位置
 */

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

public int indexOf(Object o) {
    Object[] elements = getArray();//先获取该列表,在进行操作
    return indexOf(o, elements, 0, elements.length);
}

设置元素-set

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;//操作前先进行复制
            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();//释放锁
    }
}

添加元素-add

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();//释放锁
    }
}

删除元素-remove

//删除指定位置的元素
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);//删除相应位置元素
}

对象克隆以及锁重置-clone

public Object clone() {
    try {
        @SuppressWarnings("unchecked")
        CopyOnWriteArrayList<E> clone =
            (CopyOnWriteArrayList<E>) super.clone();
        clone.resetLock();//重置锁状态
        return clone;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
}


private void resetLock() {
    UNSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock());//根据lock在对象内存地址中的位置重置lock锁
}
private static final sun.misc.Unsafe UNSAFE;
private static final long lockOffset;//获取内存中
static {
    try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();//获取unsafe对象
        Class<?> k = CopyOnWriteArrayList.class;
        lockOffset = UNSAFE.objectFieldOffset
            (k.getDeclaredField("lock"));//成员属性在内存中的地址相对于对象内存地址的偏移量
    } catch (Exception e) {
        throw new Error(e);
    }
}

其它的方法,如get、toArray、isEmpty、size、contain、foreach、iterator方法都是没有同步操作的,他们的代码与arraylist相似,只是在操作前会先将元素通过getArray方法获取,保存到临时数组,对临时数组进行操作。只有对列表进行添加(add、addAll)、删除(remove、removeAll)、清除(clear)、更新(set)、排序(sort)需要加锁同步,另外当对当前对象进行序列化以及克隆时,序列化后的对象以及克隆得到的对象需要重置锁的状态。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值