二. 集合学习之CopyOnWriteArrayList

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二. 集合学习之CopyOnWriteArrayList相关的知识,希望对你有一定的参考价值。

本博文主要记录CopyOnWriteArrayList的使用,通过对ArrayList的对比了解CopyOnWriteArrayList具有什么样的特性,并了解其内部实现原理。

 

在你的应用中有一个列表(List),它被频繁的遍历,但是很少被修改。像“你的主页上的前十个分类,它被频繁的访问,但是每个小时通过Job来调度更新”。

如果你使用ArrayList来作为该列表的数据结构并且不使用同步(synchronization),你可能会遇到ConcurrentModificationException,因为在你使用Job修改该列表时,其他的代码可能正在遍历该列表。

    有些开发人员可能使用Vector或Collections.synchronizedList(List<T>)的方式来解决该问题。但是这并没有效果!虽然在列表上add(),remove()和get()方法现在对线程是安全的,但遍历时仍然会抛出ConcurrentModificationException!在你遍历在列表时,你需要在该列表上使用同步,同时,在修改它时,也需要使用同步机制。这对性能和可扩展性来说是一个噩梦。同步需要在所有的地方出现,仅仅是因为每个小时都需要做更新。

 

幸运的是,这里有更好的解决方案。使用CopyOnWriteArrayList。

 

CopyOnWriteArrayList是jdk concurrent包中提供的一个非阻塞型的,线程安全的List实现。

CopyOnWriteArrayList在进行数据修改时,都不会对数据进行锁定,每次修改时,先拷贝整个数组,然后修改其中的一些元素,完成上述操作后,替换整个数组的指针。
对CopyOnWriteArrayList进行读取时,也不进行数据锁定,直接返回需要查询的数据,如果需要返回整个数组,那么会将整个数组拷贝一份,再返回,保证内部array在任何情况下都是只读的。
 

在CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过JDK1.6的Arrays.copyof()来生成一份新的数组

然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象,这样保证了每次写都是在新的对象上(因为要保证写的一致性,这里要对各种写操作要加一把锁,JDK1.6在这里用了重入锁),

然后读的时候就是在引用的当前对象上进行读(包括get,iterator等),不存在加锁和阻塞,针对iterator使用了一个叫 COWIterator的阉割版迭代器,因为不支持写操作,当获取CopyOnWriteArrayList的迭代器时,是将迭代器里的数据引用指向当前引用指向的数据对象,无论未来发生什么写操作,都不会再更改迭代器里的数据对象引用,所以迭代器也很安全。

 

CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之间也不需要加锁,读和写之间的同步处理只是在写完后通过一个简单的“=”将引用指向新的数组对象上来,这个几乎不需要时间,这样读操作就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException ,所以最后得出结论:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

 


正因为上述读写特性,如果需要频繁对CopyOnWriteArrayList进行修改,而很少读取的话,那么会严重降低系统性能。
因为没有锁的干预,所以CopyOnWriteArrayLIst在少量修改,频繁读取的场景下,有很好的并发性能。
 

以上是关于二. 集合学习之CopyOnWriteArrayList的主要内容,如果未能解决你的问题,请参考以下文章

Redis学习之set类型总结

Spark-core学习之二 Spark-core

Kotlin 学习之被我一直用错的“return@forEachIndexed/return@forEach”

Kotlin 学习之被我一直用错的“return@forEachIndexed/return@forEach”

Kotlin 学习之被我一直用错的“return@forEachIndexed/return@forEach”

python学习之集合