前两天面试遇到的问题,arrayList本身是线程不安全的,但是他的查询快,因为底层是数组,根据下标找很方便,我们想用他的性能,又想保证安全性该怎么处理?

解决方案如下:

方案一: 通过collections.synchronizedList方法将ArrayList转化为线程安全的,相关源码如下:

arraylist remove线程安全吗 arraylist如何保证线程安全_数组

其中,涉及到get、set、add、remove,indexOf、lastIndexOf,addAll等方法都添加了同步锁synchronized,另外上图中最后一行代码的注解还在告知我们,类似Iterator方法等无法保证线程安全。效率也并不好,感觉跟vector没什么区别。

方案二:

通过CopyOnWriteArrayList方法封装ArrayList

见名知意  通过复制写的操作来进行处理,可以理解为数据库的读写分离 ,对查询get操作不进行加锁处理,对修改,新增方法添加锁

参考源码如下

第一部分:先定义出数组,之后在增删操作的时候对数组进行扩展

arraylist remove线程安全吗 arraylist如何保证线程安全_线程安全_02

第二部分,以add为例,查看处理逻辑

arraylist remove线程安全吗 arraylist如何保证线程安全_多线程_03

在进行add的时候,分为在数组的最末端添加和指定下标的添加方法

最末端添加的时候,将原数组的长度+1,直接复制返回。

指定下标添加的时候,判断是否为最末端,不是最末端的话将数组通过当前下标分为两部分,前半部分先转换,后半部分将新的元素塞进去,然后返回,并且在方法的开始和结束都有lock锁的处理

之后我们看看查询方法:

arraylist remove线程安全吗 arraylist如何保证线程安全_多线程_04

并未加锁。

 

在copyOnWrite方法中,我们在查询时的性能,效率一定比方案一好,当然,在进行修改操作的时候并效率其实是一样的。

在开发中并没有十全十美的方法能在保证性能的情况下保证安全,我们只能尽量保证利益最大化,  另外我在方案二中的开头有专门把array这个数组给截出来,是希望自己记住,多线程处理的情况下,共享数据最好添加一个volatitle和transient来保证数据的可见性和安全性。