首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程並發容器:ConcurrentHashMap 與 CopyOnWriteArrayList 深度解析

     編輯:Java 相關知識

在 Java 多線程開發中,選擇正確的並發容器至關重要。本文深度解析兩大最常用並發容器 —— ConcurrentHashMap 與 CopyOnWriteArrayList 的實現原理、適用場景、性能特徵與常見誤區。

ConcurrentHashMap:高併發 Map 的首選

JDK 8 核心實現

  • 數組 + 鏈表/紅黑樹:同 HashMap,但採用 CAS + synchronized 細粒度鎖。
  • 鍵值不可為 null:避免歧義(get 返回 null 可能是不存在或值為 null)。
  • 擴容機制:多線程協助擴容,避免全表鎖定。

關鍵操作語義

適用場景

  • 高併發讀寫 Map(緩存、計數器、註冊表)。
  • 需原子複合操作(如 compute、merge、putIfAbsent)。
  • 替代 Hashtable、Collections.synchronizedMap。

常見誤區

  1. 複合操作非原子:if (!map.containsKey(k)) map.put(k, v) 非線程安全,必須用 putIfAbsent 或 computeIfAbsent。
  2. size() 不精確:並發下僅作參考,統計用 mappingCount()(long)。
  3. 迭代器弱一致:遍歷過程中新增/刪除元素可能不反映、不拋異常。

CopyOnWriteArrayList:讀多寫少的列表神器

核心原理

  • 寫時復制:任何修改操作(add/set/remove)都創建新數組副本,舊數組供讀線程繼續使用。
  • 讀無鎖:get/iterator 直接訪問當前數組,極快。
  • 迭代器快照:構建迭代器時鎖定當前數組,後續修改不影響已有迭代器。

性能特徵

方法線程安全性備註
get(key)無鎖,volatile 讀高性能,可能讀到舊值(弱一致性)
put(key, val)CAS + synchronized 鎖頭節點僅鎖定桶,並發度高
computeIfAbsent(k, f)原子複合操作推薦替代 putIfAbsent + get
size()近似值並發下不精確,用 mappingCount()
遍歷弱一致性迭代器不拋 ConcurrentModificationException,反映遍歷時刻快照

適用場景

  • 讀遠多於寫:事件監聽器列表、配置緩存、白名單/黑名單。
  • 需要遍歷時不被修改干擾(快照語義)。
  • 列表規模較小(< 1000)或寫入極低頻。

不適用場景

  • 寫入頻繁或列表巨大(內存/GC 壓力大)。
  • 需強一致性讀取最新寫入(讀到的是構建迭代器時的快照)。
  • 作為佇列/棧高頻操作。

選型決策表

操作時間複雜度說明
get/遍歷O(1) / O(n)無鎖,極快
add/set/removeO(n)需復制整數組,寫入慢
內存寫入瞬間雙倍大列表頻繁寫入易觸發 GC
需求推薦容器替代方案
高併發 Map 讀寫ConcurrentHashMapConcurrentSkipListMap (有序)
線程安全 List,讀多寫少CopyOnWriteArrayListCollections.synchronizedList + 手動同步遍歷
高併發佇列ConcurrentLinkedQueueArrayBlockingQueue (有界)
有序 MapConcurrentSkipListMapTreeMap + 鎖

最佳實踐總結

  1. 默認選 ConcurrentHashMap 替代所有同步 Map 需求。
  2. 監聽器/觀察者模式列表用 CopyOnWriteArrayList。
  3. 避免在 ConcurrentHashMap 上做複合邏輯,用原子方法。
  4. 大列表頻繁寫入改用 ConcurrentLinkedQueue 或加鎖 ArrayList。
  5. JDK 9+ 可用 Map.ofEntries、List.copyOf 創建不可變集合,天然線程安全。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved