美文网首页
锁性能提升——CopyOnWriteArrayList

锁性能提升——CopyOnWriteArrayList

作者: 柚子过来 | 来源:发表于2018-06-21 14:22 被阅读0次

    在说CopyOnWriteArrayList之前先说下ArrayList为什么不是线程安全的,通常意义上的线程安全是指在多个线程同时执行的 情况下不会出现数据不一致或者脏数据等,也就是多个线程一起执行与一个线程执行多次的结果应该是一样的。所以其实如果一个操作如果不是原子操作,又没有做线程安全的处理(如锁),那就会有线程安全问题。
    比如我们看ArrayList的add方法:

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;       (1)
        size = s + 1;                   (2)
    }
    

    从代码可以看出是先添加数据,再更新size,所以如果线程1执行到了(1),但是还没执行(2),这时线程2又add,那就会出现数据丢失。

    与ArrayList对应的线程安全类有Vector,但是Vector是对add、get、remove等方法添加了synchronized同步保证的线程安全,所以性能较低。

    在实际的应用场景中读操作可能远远多于写操作,所以去除get操作的同步开销可以提升性能,但是这时就要考虑到如果去除了get操作的同步,但是由于集合的fail-fast机制,如果去除了get操作的同步,如果一个线程在get,另一个线程add的话就会抛出ConcurrentModificationException异常。

    CopyOnWriteArrayList使用了CopyOnWrite的思想,在add或者remove数据的时候,先复制一份数组的副本,对副本进行add操作,然后再将实际数组的引用指向副本,完成更新:

      public boolean add(E e) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        }
    }
    
     final void setArray(Object[] a) {
        array = a;
    }
    
     public E remove(int index) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = elementAt(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;
        }
    }
    

    可以发现CopyOnWrite操作的复制行为保证了在进行写操作时,可以并发的进行读操作并且不会有并发问题。而写操作本身是加锁的,所以写操作也不会有并发问题

    但是CopyOnWriteArrayList这样实现就有弱一致性的缺点,它只能保证最终一致性,因为线程读取到的可能不是最新的数据。

    相关文章

      网友评论

          本文标题:锁性能提升——CopyOnWriteArrayList

          本文链接:https://www.haomeiwen.com/subject/pzvqyftx.html