【多线程】JUC版的CopyOnWriteArrayList

CopyOnWriteArrayList

  • CopyOnWriteArrayList适合于多线程场景下使用,其采用读写分离的思想,读操作不上锁,写操作上锁,且写操作效率较低。
  • CopyOnWriteArrayList基于fail-safe机制,每次修改都会在原先基础上复制一份,修改完毕后在进行替换
  • CopyOnWriteArrayList采用的是ReentrantLock进行上锁。
  • CopyOnWriteArrayList和ArrayList一样,其底层数据结构也是数组,加上transient不让其被序列化,加上volatile修饰来保证多线程下的其可见性和有序性。
  • CopyOnWriteArrayList效率比ArrayList低不少,毕竟多线程场景下,其每次都是要在原数组基础上复制一份在操作耗内存和时间,而ArrayList只是容量满了进行扩容,因此在非多线程的场景下还是用ArrayList吧。

构造函数

public CopyOnWriteArrayList() {
    //默认创建一个大小为0的数组
    setArray(new Object[0]);
}

final void setArray(Object[] a) {
    array = a;
}

public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    //如果当前集合是CopyOnWriteArrayList的类型的话,直接赋值给它
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        //否则调用toArra()将其转为数组   
        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;
}
public int size() {
    return getArray().length;
}
public boolean isEmpty() {
    return size() == 0;
}
public int indexOf(E e, int index) {
    Object[] elements = getArray();
    return indexOf(e, elements, index, elements.length);
}
public int lastIndexOf(Object o) {
    Object[] elements = getArray();
    return lastIndexOf(o, elements, elements.length - 1);
}

........

add方法(使用ReentrantLock加锁

public boolean add(E e) {
    //使用ReentrantLock上锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //调用getArray()获取原来的数组
        Object[] elements = getArray();
        int len = elements.length;
        //复制老数组,得到一个长度+1的数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //添加元素,在用setArray()函数替换原数组
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

可见其修改操作是基于fail-safe机制,像我们的String一样,不在原来的对象上直接进行操作,而是复制一份对其进行修改,另外此处的修改操作是利用Lock锁进行上锁的,所以保证了线程安全问题。

remove方法(使用ReentrantLock加锁

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

思路与add方法一致。

posted @ 2022-04-01 00:06  HZX↑  阅读(45)  评论(0编辑  收藏  举报