【Java】如何保证ArrayList的线程安全?

这有三种主流的实现ArrayList线程安全的方法。

一、Vector

Vector 是矢量队列,它是JDK1.0版本添加的类,历史比ArrayList(since 1.2)更为悠久。其底层和ArrayList一样是数组,除线程安全外,大多数实现方法和ArrayList逻辑基本一致。

值得一提的是,从jdk1.2以后,Java提供了系统的集合框架,就将Vector改为实现List接口,从而导致Vector里有一些重复的方法,例如:addElement(Object obj),实际上这个方法和add(Object obj)没什么区别。

Vector 的实现,就是在方法上都加上synchronized(即使get也不例外)。

/**
 * Appends the specified element to the end of this Vector.
 *
 * @param e element to be appended to this Vector
 * @return {@code true} (as specified by {@link Collection#add})
 * @since 1.2
 */
public synchronized boolean add(E e) {
          
   
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

关于Vector为什么被弃用

    所有方法都有同步开销,非多线程下,效率不如ArrayList; 一些老代码,导致有重复的方法,以及风格和新的集合类格格不入; 线程安全的实现,可以通过新的Collections.synchronizedList之类的调用来替换。

二、Collections.synchronizedList

使用collentions.synchronizedList()方法为ArrayList加锁

三、CopyOnWriteArrayList

CopyOnWriteArrayList是1.5后引入,属于JUC的一部分。他基本的原理还是和ArrayList一样,涉及线程安全的部分,是通过写时复制的方式来实现(从名字中就可以看出)。它内部有个volatile数组来保持数据。在“添加/修改/删除”数据时,会先获取互斥锁,再新建一个数组,并将更新后的数据拷贝到新建的数组中,最后再将该数组赋值给volatile数组,然后再释放互斥锁。

因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。

四、性能比较

    读操作各方式基本没有区别。 写时CopyOnWriteArrayList性能较差,且随着数据量的增大,几何级下跌。 CopyOnWriteArrayList,适用于以读为主,读操作远远大于写操作的场景中使用,比如缓存。 Collections.synchronizedList则可以用在CopyOnWriteArrayList不适用但是有需要同步的地方使用,比如读写操作都比较均匀的地方。
经验分享 程序员 微信小程序 职场和发展