锁性能提升——CopyOnWriteArrayList

2018-06-21  本文已影响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这样实现就有弱一致性的缺点,它只能保证最终一致性,因为线程读取到的可能不是最新的数据。

上一篇下一篇

猜你喜欢

热点阅读