CopyOnWriteArrayList 怎么用?答不上來直接回家!
引言
面試官:你對 CopyOnWriteArrayList 了解嗎?
小米(滿臉自信):當然,它可是并發(fā)編程里的“溫柔派”!
面試官:哦?怎么個溫柔法?
小米(推了推眼鏡):我給你講個故事你就明白了……
故事開篇:一個神奇的會議室
在一個互聯(lián)網(wǎng)大廠的高樓里,有一間特別的會議室——寫時復(fù)制會議室(CopyOnWrite Meeting Room)。
這間會議室很有意思,每次會議開始前,房間管理員都會把之前的會議記錄“復(fù)制”一份,只有會議主持人(寫操作)才能在新記錄上修改,而參會者(讀操作)依舊查看舊版本的記錄,互不干擾。等主持人修改完畢,大家才會看到最新的會議紀要。
這種方式確保了會議的穩(wěn)定性,避免了混亂,但同時也有一些缺點,比如復(fù)制過程會消耗一定的時間和內(nèi)存。
這,就是 CopyOnWriteArrayList 背后的理念!
CopyOnWriteArrayList 是什么?
CopyOnWriteArrayList 是 Java 并發(fā)工具包(java.util.concurrent)提供的一個線程安全的 ArrayList,它的核心機制是寫時復(fù)制(Copy-On-Write,簡稱 COW)。
簡單來說:
- 讀操作(get()):不加鎖,直接讀取舊數(shù)據(jù),速度極快。
- 寫操作(add()、set()、remove()):會創(chuàng)建當前數(shù)組的副本,在副本上進行修改,修改完成后再更新引用。
它的底層實現(xiàn)可以概括為:
- 讀取操作 直接訪問數(shù)組,不需要加鎖。
- 寫入操作 復(fù)制一個新數(shù)組,在新數(shù)組上進行修改,然后用 volatile 變量更新數(shù)組引用,確??梢娦?。
CopyOnWriteArrayList 的核心代碼
圖片
可以看到,每次 add() 操作都會復(fù)制一個新數(shù)組,而不是直接修改原數(shù)組。
應(yīng)用場景:什么時候適合使用 CopyOnWriteArrayList?
CopyOnWriteArrayList 非常適合讀多寫少的場景,例如:
1. 訂閱-通知模式
在觀察者模式(Observer Pattern)中,通常有多個訂閱者(讀操作)在監(jiān)聽一個事件(寫操作)。CopyOnWriteArrayList 能夠保證在通知所有訂閱者時,不會因為訂閱列表的變更而發(fā)生并發(fā)問題。
示例:
圖片
因為 CopyOnWriteArrayList 在迭代過程中不會受到修改影響,所以可以安全地在多線程環(huán)境下進行事件通知。
2. 黑名單、白名單
在一些安全場景中,例如:
- 黑名單(訪問控制列表):需要高頻率檢查 IP 是否在黑名單里,但黑名單更新較少。
- 商品推薦白名單:用戶查詢商品推薦列表的頻率遠高于修改白名單的頻率。
示例:
圖片
讀操作不會被鎖住,查詢速度極快,而寫操作雖然成本較高,但由于修改次數(shù)較少,整體效率依然很高。
3. 系統(tǒng)配置的動態(tài)更新
在某些業(yè)務(wù)系統(tǒng)中,配置項可能需要動態(tài)更新,但讀取這些配置的頻率遠高于修改的頻率。例如:
- AB 測試參數(shù)
- 限流規(guī)則
- 灰度發(fā)布的用戶名單
使用 CopyOnWriteArrayList 可以確保更新配置時不會影響業(yè)務(wù)邏輯的穩(wěn)定性。
示例:
圖片
CopyOnWriteArrayList 的優(yōu)缺點
優(yōu)點
1、讀操作無鎖,性能極高
- 由于 get() 操作直接讀取 volatile 變量,查詢速度接近普通 ArrayList,沒有鎖競爭。
2、迭代器不會拋出 ConcurrentModificationException
- 由于 CopyOnWriteArrayList 在修改時會創(chuàng)建新數(shù)組,而 iterator() 返回的迭代器是基于舊數(shù)組的,因此不會出現(xiàn) ConcurrentModificationException。
3、適用于讀多寫少的場景
- 例如訂閱-通知模式、黑名單、緩存、配置等應(yīng)用場景。
缺點
1、寫操作成本高
- 每次寫操作都會創(chuàng)建一個新數(shù)組,數(shù)據(jù)量大時會帶來較大的內(nèi)存消耗和 GC 壓力。
2、無法保證實時一致性
- 由于讀取的是舊數(shù)組,修改后不會立刻對所有線程可見,而是等到下一次獲取時才會看到新數(shù)據(jù)。
3、適用場景受限
- 由于寫時復(fù)制的特性,它不適合寫操作頻繁的場景,例如高并發(fā)的隊列操作、計數(shù)器等。
總結(jié):面試官的最終評價
面試官(點頭微笑): 你這個“溫柔派”類比很有意思,的確,CopyOnWriteArrayList 適用于讀多寫少的場景,雖然寫操作比較昂貴,但它的線程安全性和無鎖讀的優(yōu)勢,在某些場景下是無可替代的。
小米(得意地笑): 多謝夸獎!不過如果是寫多讀少的情況,那我更推薦 ConcurrentHashMap 或 BlockingQueue 之類的方案啦!
面試官(滿意地伸出手): 很棒,歡迎加入我們團隊!
總結(jié)
圖片
CopyOnWriteArrayList 就像一個會議記錄員,每次修改都復(fù)制一份新紀錄,讓讀者查看舊版本,寫入者改動新版本,最終統(tǒng)一替換。它的優(yōu)雅之處,在于通過空間換時間,換取了并發(fā)環(huán)境下的高效讀操作。
所以,下次面試官問你:
“CopyOnWriteArrayList 適合什么場景?”
你就大聲說:適合讀多寫少!