【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不适用但是有需要同步的地方使用,比如读写操作都比较均匀的地方。
下一篇:
【2023蓝桥杯省模拟赛】字母数