JAVA并发容器:CopyOnWriteArrayList与CopyOnWriteArraySet

生活

所有的程序员都剧作家,而所有计算机都是糟糕的演员。

CopyOnWriteArrayList介绍

还记得学集合的时候,学的第一个集合就是ArrayList.它是一个由数组实现的集合。因为他对数组的增删改和查询都是不加锁的,所以它并不是线程安全的。
因此,我们会引入到一个线程安全的集合,Vector,他的底层也是数组,与ArrayList不同的是,Vector里对元素的增删改查都是加了synchronized,因此说他是线程安全的。但是这种在任何场景都加锁的集合显然效率不高。
因此在多线程高并发环境下,引入了今天要研究的CopyOnWriteArrayList,他是一个写时复制的一个集合,具体来说,它的底层还是一个数组,在查询的时候直接查这个数组里的元素,不需要加锁,但是在做增删改操作时,会获取到互斥锁,并且copy原数组的数据到新数组【在新数组内进行增删改操作】,执行完这些操作后,把集合数组引用指向新数组,因此在增删改下不会对查询产生影响,但是因为在做增删改操作时每次都需要复制新数组,非常耗费性能,因此这个集合适用在多读少写的场景,比如缓存。
注意:如果你希望能马上读到刚写进去的数据,就不适用这个集合了。

成员

    //互斥锁
    transient final ReentrantLock lock = new ReentrantLock();

    //容器数组
    private volatile transient Object[] array;

CopyOnWriteArrayList方法源码解析

简单的看了一下CopyOnWriteArrayList的源码,非常简单而且通俗易懂,这里就贴一下增加数据和查询数据的操作,主要理解一个解锁,一个不加锁,以及写时复制的实现。

添加元素:
 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();
        }
    }

查询

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

    /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
     //可以看到确实不加锁
    public E get(int index) {
        return get(getArray(), index);
    }

CopyOnWriteArraySet

CopyOnWriteArraySet 是HashSet的并发实现。底层就是使用CopyOnWriteArrayList,要知道Set是不包含重复元素的,因此可以看到它底层实际添加元素的方法是调用了CopyOnWriteArrayList
addIfAbsent

public boolean addIfAbsent(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // Copy while checking if already present.
            // This wins in the most common case where it is not present
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = new Object[len + 1];
            for (int i = 0; i < len; ++i) {
            //如果有相同元素,直接放弃添加
                if (eq(e, elements[i]))
                    return false; // exit, throwing away copy
                else
                    newElements[i] = elements[i];
            }
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84781577