「線程安全」是 Java 併發編程的核心概念,但常被誤解為「加鎖就安全」或「非線程安全就不能用」。本文從定義本質、典型容器對比、性能代價、選擇決策矩陣、以及現代併發包替代方案五個維度,構建完整的線程安全認知體系。
Brian Goetz《Java Concurrency in Practice》定義:當多個線程訪問某個類時,不管運行時環境採用何種調度方式、線程如何交替執行,主調代碼不需要任何額外同步或協調,這個類都能表現出正確行為,那麼這個類就是線程安全的。
關鍵點:
| 非線程安全 | 線程安全(舊API) | 線程安全(現代推薦) | 適用場景 |
|---|---|---|---|
ArrayList | Vector | CopyOnWriteArrayList | 讀多寫少、遍歷頻繁 |
LinkedList | - | - | 單線程/外部同步 |
HashMap | Hashtable | ConcurrentHashMap | 高併發 Map,JDK8+ 性能極佳 |
StringBuilder | StringBuffer | - | 字符串拼接,單線程用 Builder |
HashSet | - | CopyOnWriteArraySet / ConcurrentHashMap.newKeySet() | 併發 Set |
synchronized,粗粒度鎖,單線程/低併發下性能損耗巨大(鎖獲取/釋放、內存屏障、上下文切換開銷)。ConcurrentHashMap:JDK 7 分段鎖 → JDK 8 CAS + synchronized + 紅黑樹,細粒度鎖,讀操作無鎖,極高吞吐。CopyOnWriteArrayList/Set:寫時復制底層數組,讀完全無鎖,適合「讀多寫極少、容忍短暫不一致」場景。LongAdder/DoubleAdder:分段計數器,高併發計數比 AtomicLong 快數量級。ArrayList、HashMap、StringBuilder。無同步開銷,性能最優。CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap(读無鎖)。ConcurrentHashMap、ConcurrentLinkedQueue、LongAdder。if (!map.containsKey(k)) map.put(k, v)):
ConcurrentHashMap.computeIfAbsent、putIfAbsent 等原子方法。ConcurrentHashMap、ConcurrentLinkedQueue):允許並發修改,不拋 ConcurrentModificationException,可能不反映最新狀態。CopyOnWrite 系列):遍歷時見不到後續寫入,絕對安全。Vector 是性能自殺。ConcurrentHashMap 單個方法原子,但 get-then-put 組合非原子。String、Integer、Guava ImmutableList、自定義 final 字段無 setter 類。優先使用不可變模式。線程安全是類封裝同步策略、調用者無感知的承諾。非線程安全類在單線程/外部同步場景完全可用且性能更優。現代 Java 開發應優先選擇 java.util.concurrent 包下的高性能併發容器,根據讀寫比例、一致性要求、複合操作需求精準匹配,而非簡單二元選擇。