Java中CopyOnWriteArrayList的使用解析
一、CopyOnWriteArrayList介紹
①、CopyOnWriteArrayList,寫數(shù)組的拷貝,支持高效率并發(fā)且是線程安全的,讀操作無鎖的ArrayList。所有可變操作都是通過對(duì)底層數(shù)組進(jìn)行一次新的復(fù)制來實(shí)現(xiàn)。
②、CopyOnWriteArrayList適合使用在讀操作遠(yuǎn)遠(yuǎn)大于寫操作的場(chǎng)景里,比如緩存。它不存在擴(kuò)容的概念,每次寫操作都要復(fù)制一個(gè)副本,在副本的基礎(chǔ)上修改后改變Array引用。CopyOnWriteArrayList中寫操作需要大面積復(fù)制數(shù)組,所以性能肯定很差。
③、CopyOnWriteArrayList 合適讀多寫少的場(chǎng)景,不過這類慎用 ,因?yàn)檎l也沒法保證CopyOnWriteArrayList 到底要放置多少數(shù)據(jù),萬一數(shù)據(jù)稍微有點(diǎn)多,每次add/set都要重新復(fù)制數(shù)組,這個(gè)代價(jià)實(shí)在太高昂了。在高性能的互聯(lián)網(wǎng)應(yīng)用中,這種操作分分鐘引起故障。
二、CopyOnWriteArrayList 有幾個(gè)缺點(diǎn)
1、由于寫操作的時(shí)候,需要拷貝數(shù)組,會(huì)消耗內(nèi)存,如果原數(shù)組的內(nèi)容比較多的情況下,可能導(dǎo)致young gc或者full gc。 (1、young gc :年輕代(Young Generation):對(duì)象被創(chuàng)建時(shí),內(nèi)存的分配首先發(fā)生在年輕代(大對(duì)象可以直接被創(chuàng)建在年老代),大部分的對(duì)象在創(chuàng)建后很快就不再使用,因此很快變得不可達(dá),于是被年輕代的GC機(jī)制清理掉(IBM的研究表明,98%的對(duì)象都是很快消亡的),這個(gè)GC機(jī)制被稱為Minor GC或叫Young GC。
2、年老代(Old Generation):對(duì)象如果在年輕代存活了足夠長(zhǎng)的時(shí)間而沒有被清理掉(即在幾次Young GC后存活了下來),則會(huì)被復(fù)制到年老代,年老代的空間一般比年輕代大,能存放更多的對(duì)象,在年老代上發(fā)生的GC次數(shù)也比年輕代少。當(dāng)年老代內(nèi)存不足時(shí),將執(zhí)行Major GC,也叫 Full GC )
3、不能用于實(shí)時(shí)讀的場(chǎng)景,像拷貝數(shù)組、新增元素都需要時(shí)間,所以調(diào)用一個(gè)set操作后,讀取到數(shù)據(jù)可能還是舊的,雖然CopyOnWriteArrayList 能做到最終一致性,但是還是沒法滿足實(shí)時(shí)性要求;
三、CopyOnWriteArrayList的一些方法
1、add(E e) :將指定元素添加到此列表的尾部,返回值為boolean。
2、add(int index, E element) : 在此列表的指定位置上插入指定元素。
3、clear():從此列表移除所有元素。
4、contains(Object o) :如果此列表包含指定的元素,則返回 true。
5、equals(Object o) :比較指定對(duì)象與此列表的相等性。
6、get(int index) : 返回列表中指定位置的元素。
7、hashCode() : 返回此列表的哈希碼值。
8、indexOf(E e, int index) : 返回第一次出現(xiàn)的指定元素在此列表中的索引,從 index 開始向前搜索,如果沒有找到該元素,則返回 -1。
9、indexOf(Object o) :返回此列表中第一次出現(xiàn)的指定元素的索引;如果此列表不包含該元素,則返回 -1。
10、isEmpty() :如果此列表不包含任何元素,則返回 true。
11、iterator() :返回以恰當(dāng)順序在此列表元素上進(jìn)行迭代的迭代器,返回值為 Iterator。
12、lastIndexOf(E e, int index) :返回最后一次出現(xiàn)的指定元素在此列表中的索引,從 index 開始向后搜索,如果沒有找到該元素,則返回 -1。
13、lastIndexOf(Object o) : 返回此列表中最后出現(xiàn)的指定元素的索引;如果列表不包含此元素,則返回 -1。
14、remove(int index) :移除此列表指定位置上的元素。
15、remove(Object o) :從此列表移除第一次出現(xiàn)的指定元素(如果存在),返回值為 boolean。
16、set(int index, E element) :用指定的元素替代此列表指定位置上的元素。
17、size() :返回此列表中的元素?cái)?shù)。
18、subList(int fromIndex, int toIndex) :返回此列表中 fromIndex(包括)和 toIndex(不包括)之間部分的視圖,返回值為 List 。
四、Java代碼示例
package chapter3.copyonwrite; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * @author czd */ public class CopyOnWriteArrayListTest { public static void main(String[] args) { /** * 構(gòu)造方法摘要 * CopyOnWriteArrayList(): * 創(chuàng)建一個(gè)空列表。 * CopyOnWriteArrayList(Collection<? extends E> c): * 創(chuàng)建一個(gè)按 collection 的迭代器返回元素的順序包含指定 collection 元素的列表。 * CopyOnWriteArrayList(E[] toCopyIn): * 創(chuàng)建一個(gè)保存給定數(shù)組的副本的列表。 */ /** * 1、add(E e) :將指定元素添加到此列表的尾部,返回值為boolean。 * * 2、iterator() :返回以恰當(dāng)順序在此列表元素上進(jìn)行迭代的迭代器,返回值為Iterator<E>。 */ CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>(); Boolean addBoolean = copyOnWriteArrayList.add(1); System.out.println("是否添加到此列表的尾部?" + addBoolean); Iterator<Integer> iterator = copyOnWriteArrayList.iterator(); while (iterator.hasNext()){ System.out.println("iterator的結(jié)果: " + iterator.next()); } /** * 3、add(int index, E element) :在此列表的指定位置上插入指定元素。 */ CopyOnWriteArrayList<Integer> copyOnWriteArrayList1 = new CopyOnWriteArrayList<>(); copyOnWriteArrayList1.add(1); copyOnWriteArrayList1.add(2); copyOnWriteArrayList1.add(2,5); // copyOnWriteArrayList1.add(4,6); System.out.println("index = 0 的值為:" + copyOnWriteArrayList1.get(0)); System.out.println("index = 1 的值為:" + copyOnWriteArrayList1.get(1)); System.out.println("index = 2 的值為:" + copyOnWriteArrayList1.get(2)); // System.out.println("index = 4 的值為:" + copyOnWriteArrayList1.get(4)); /** * 注意:如果使用add(int index, E element),必須保證index的前面的index存在值,不然會(huì)報(bào)錯(cuò)。 * (可以把上兩行注釋去掉,運(yùn)行試試結(jié)果) */ /** * 4.1、indexOf(E e, int index) * 返回第一次出現(xiàn)的指定元素在此列表中的索引,從 index 開始向前搜索,如果沒有找到該元素,則返回 -1。 * 4.2、indexOf(Object o) * 返回此列表中第一次出現(xiàn)的指定元素的索引;如果此列表不包含該元素,則返回 -1。 */ CopyOnWriteArrayList<Integer> copyOnWriteArrayList2 = new CopyOnWriteArrayList<>(); copyOnWriteArrayList2.add(1); copyOnWriteArrayList2.add(2); copyOnWriteArrayList2.add(3); Integer indexFirst = copyOnWriteArrayList2.indexOf(2); System.out.println("2的index為:" + indexFirst); /** * 5.1、lastIndexOf(E e, int index) * 返回最后一次出現(xiàn)的指定元素在此列表中的索引,從 index 開始向后搜索,如果沒有找到該元素,則返回 -1。 * 5.2、lastIndexOf(Object o) * 返回此列表中最后出現(xiàn)的指定元素的索引;如果列表不包含此元素,則返回 -1。 */ CopyOnWriteArrayList<Integer> copyOnWriteArrayList3 = new CopyOnWriteArrayList<>(); copyOnWriteArrayList3.add(1); copyOnWriteArrayList3.add(2); copyOnWriteArrayList3.add(3); copyOnWriteArrayList3.add(3); copyOnWriteArrayList3.add(4); Integer lastIndexOf = copyOnWriteArrayList3.lastIndexOf(3); System.out.println("列表中最后出現(xiàn)的指定元素的索引: " + lastIndexOf); /** * 6、remove(int index) :移除此列表指定位置上的元素,并且此下標(biāo)后面的值會(huì)進(jìn)一位, * 即index為1的值被remove掉,index為2的值就變?yōu)閕ndex為1的值。 */ CopyOnWriteArrayList<Integer> copyOnWriteArrayList4 = new CopyOnWriteArrayList<>(); copyOnWriteArrayList4.add(5); copyOnWriteArrayList4.add(6); copyOnWriteArrayList4.add(7); copyOnWriteArrayList4.add(8); Integer removeResult = copyOnWriteArrayList4.remove(1); System.out.println("copyOnWriteArrayList4索引為1的值:" + removeResult); System.out.println("copyOnWriteArrayList4中是否還存在索引為1的值:" + copyOnWriteArrayList4.get(1)); /** * 7、remove(Object o) :從此列表移除第一次出現(xiàn)的指定元素(如果存在)。 */ CopyOnWriteArrayList<String> copyOnWriteArrayList5 = new CopyOnWriteArrayList<>(); copyOnWriteArrayList5.add("5"); copyOnWriteArrayList5.add("6"); copyOnWriteArrayList5.add("6"); copyOnWriteArrayList5.add("7"); copyOnWriteArrayList5.add("7"); System.out.println("沒被remove前的size: " + copyOnWriteArrayList5.size()); Boolean removeBoolean = copyOnWriteArrayList5.remove("7"); System.out.println("copyOnWriteArrayList5是否移除7成功?" + removeBoolean); System.out.println("index為3的值為:" + copyOnWriteArrayList5.get(3)); System.out.println("被remove后的size: " + copyOnWriteArrayList5.size()); /** * 8、set(int index, E element) :用指定的元素替代此列表指定位置上的元素。 */ CopyOnWriteArrayList<String> copyOnWriteArrayList6 = new CopyOnWriteArrayList<>(); copyOnWriteArrayList6.add("1"); copyOnWriteArrayList6.add("2"); copyOnWriteArrayList6.add("3"); copyOnWriteArrayList6.add("4"); copyOnWriteArrayList6.add("5"); System.out.println("沒使用set()方法前,index = 1 的值為:" + copyOnWriteArrayList6.get(1)); copyOnWriteArrayList6.set(1,"czd"); System.out.println("使用set()方法后,index = 1 的值為:" + copyOnWriteArrayList6.get(1)); } }
五、總結(jié)
CopyOnWriteArrayList這是一個(gè)ArrayList的線程安全的變體,其原理大概可以通俗的理解為:初始化的時(shí)候只有一個(gè)容器,很長(zhǎng)一段時(shí)間,這個(gè)容器數(shù)據(jù)、數(shù)量等沒有發(fā)生變化的時(shí)候,大家(多個(gè)線程),都是讀取(假設(shè)這段時(shí)間里只發(fā)生讀取的操作)同一個(gè)容器中的數(shù)據(jù),所以這樣大家讀到的數(shù)據(jù)都是唯一、一致、安全的,但是后來有人往里面增加了一個(gè)數(shù)據(jù),這個(gè)時(shí)候CopyOnWriteArrayList 底層實(shí)現(xiàn)添加的原理是先copy出一個(gè)容器(可以簡(jiǎn)稱副本),再往新的容器里添加這個(gè)新的數(shù)據(jù),最后把新的容器的引用地址賦值給了之前那個(gè)舊的的容器地址,但是在添加這個(gè)數(shù)據(jù)的期間,其他線程如果要去讀取數(shù)據(jù),仍然是讀取到舊的容器里的數(shù)據(jù)。
到此這篇關(guān)于Java中CopyOnWriteArrayList的使用解析的文章就介紹到這了,更多相關(guān)CopyOnWriteArrayList的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Maven進(jìn)行依賴漏洞檢查的實(shí)現(xiàn)指南
在現(xiàn)代軟件開發(fā)中,開源庫(kù)的使用變得愈加普遍和重要,然而,這些開源庫(kù)中的漏洞往往會(huì)成為潛在的安全風(fēng)險(xiǎn),在本文中,我們將探討如何使用 Maven 進(jìn)行依賴漏洞檢查,以確保項(xiàng)目的安全性和穩(wěn)定性,需要的朋友可以參考下2024-05-05Spring實(shí)現(xiàn)內(nèi)置監(jiān)聽器
這篇文章主要介紹了Spring 實(shí)現(xiàn)自定義監(jiān)聽器案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧,希望能給你帶來幫助2021-07-07Mybatis-Plus實(shí)體類繼承Model的使用小結(jié)
Mybatis-Plus實(shí)體類繼承Model的使用是Mybatis-Plus中的一個(gè)重要特性,它允許開發(fā)者通過繼承Model類來快速實(shí)現(xiàn)一些通用的功能,本文主要介紹了Mybatis-Plus實(shí)體類繼承Model的使用小結(jié),感興趣的可以了解一下2024-07-07SpringMVC的簡(jiǎn)單傳值(實(shí)現(xiàn)代碼)
下面小編就為大家?guī)硪黄猄pringMVC的簡(jiǎn)單傳值(實(shí)現(xiàn)代碼)。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05Java長(zhǎng)度不足左位補(bǔ)0的3種實(shí)現(xiàn)方法
這篇文章主要介紹了Java長(zhǎng)度不足左位補(bǔ)0的3種實(shí)現(xiàn)方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12springboot使用spring-data-jpa操作MySQL數(shù)據(jù)庫(kù)
這篇文章主要介紹了springboot使用spring-data-jpa操作MySQL數(shù)據(jù)庫(kù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07