CopyOnWriteArrayList 循环中remove
ArrayList不是线程安全的,所以如果需要保证ArrayList在多线程环境下的线程安全,即保证读的线程可见性和写的数据一致性,可以使用synchronized或者ReentrantLock对ArrayList的读写进行同步,或者使用Collections.syncrhonizedList来将ArrayList包装成SynchronizedList。
由于以上方法对读写都需要加锁,一定程度上影响了读写操作的并发性能和吞吐量,不过如果读写操作的频率不确定,即读写都可能非常频繁,则就不得不使用以上方法来保证ArrayList的线程安全性。
如果存在以读为主,写非常少,基本不存在写操作,如添加元素,删除元素等,则可以考虑使用CopyOnWriteArrayList。这是一个线程安全版本的ArrayList,由命名可以知道,CopyOnWriteArrayList在写操作的时候,包括添加,删除元素等,会新建一个列表,然后将当前列表拷贝拷贝到这个新列表,最后使用这个新列表替换旧列表。
CopyOnWriteArrayList底层也是使用一个数组来存放数据的,在读写方法,读操作是不加锁的,写操作需要使用一个ReentrantLock来加锁,从而对多个写线程进行同步,同时底层数组也是使用volatile修饰的,则保证了读写线程之间的可见性。除此之外,CopyOnWriteArrayList的迭代器不是fail-fast的,即写操作不会影响迭代器的数据遍历。
以下以get操作为例,分析以下读操作:读操作是直接从内部存放数据的数组读取数据的,不需要加锁。
ArrayList的迭代器是fail-fast的,即如果一个线程在通过ArrayList的迭代器遍历列表数据时,如果其他线程修改了该列表,则该迭代器线程会抛ConcurrentModifyException的异常。而CopyOnWriteArrayList的迭代器是不受其他线程并发修改的影响的。
CopyOnWriteArrayList在返回一个迭代器的时候,会基于创建这个迭代器的时候,内部数组所拥有的数据,创建一个该内部数组当前的快照,然后迭代器遍历的是该快照,而不是内部的数组。所以这种实现方式也存在一定的数据延迟性,即对其他线程并行添加的数据不可见。不过CopyOnWriteArrayList是基于写操作很少或者基本没有的场景的,所以这种实现方法在这种假设下可行。
因为迭代器遍历的是内部数组的快照副本,故与ArrayList的迭代器不同的是,CopyOnWriteArrayList的迭代器是不支持写操作的,如添加,删除数据等。
写操作是需要通过ReentrantLock这个互斥锁来进行加锁的,然后会创建一个新的数组来替换原来的数组。由于写操作很少,所以对于添加元素,新数组大小递增1,这个与ArrayList的每次扩容为原来的1.5倍是不一样的。对于删除元素,新数组大小递减1。如下为add方法的实现:
CopyOnWriteArrayList的子列表与COWSubList与ArrayList的子列表一样,内部使用的也是父列表的数组,主要是通过传递父列表引用给COWSubList,在COWSubList内部的读写操作是通过父列表来完成的,其中读写操作均需要使用lock加锁。
CopyOnWriteArrayList的subList返回子数组:
CopyOnWriteArraySet是基于CopyOnWriteArrayList实现的一个Set集合,内部不包含重复元素,也是线程安全的。
CopyOnWriteArraySet的定义如下:内部包含一个CopyOnWriteArrayList引用,而不是继承于CopyOnWriteArrayList来实现。
CopyOnWriteArraySet的核心实现为add添加元素时,避免元素重复,同时需要考虑多线程同时添加的问题。主要是基于CopyOnWriteArrayList的addIfAbsent实现:
CopyOnWriteArrayList的addIfAbsent实现如下:主要通过加锁成功之后,再次获取底层数组来判断是否需要添加,因为加锁成功后,只有当前线程可以访问这个底层数组,同时由于数组为volatile的,故可以保证多线程的可见性。
<< 上一篇
下一篇 >>