Java 併發編程基石:線程安全性、原子性、鎖機制與活躍性能平衡
併發編程核心在於正確管理共享可變狀態。本文系統闡述線程安全定義、三種修復策略、原子性與競態條件、內置鎖與重入、狀態保護準則、活躍性與性能優化。
線程安全定義
多線程訪問某類時,無需調用端同步,該類行為正確(符合規範),則稱線程安全。線程安全類內部封裝同步,客戶端無需額外措施。
三種修復共享可變狀態方案
- 不共享:線程封閉、棧封閉、ThreadLocal。
- 不可變:final 域、無 setter、構造後凍結(如 String、Integer)。
- 同步訪問:synchronized、volatile、顯式鎖、原子變量。
無狀態對象(如大多數 Servlet)天然線程安全。
原子性與競態條件
- 競態條件:多線程交替執行順序影響結果正確性。
- 複合操作非原子:
i++ = 讀取、加一、寫回三步,中間可被打斷。
- 延遲初始化:將對象創建推遲到首次使用,需保證只初始化一次(雙重檢查鎖、static 內部類、AtomicReference)。
- 原子變量類:
java.util.concurrent.atomic 包提供 AtomicInteger 等,基於 CAS 實現無鎖原子更新。
鎖機制
內置鎖
synchronized (lock) {
// 訪問/修改共享狀態
}
- 每個對象一把鎖(監視器鎖),互斥:同一時刻僅一線程持有。
- 進入同步塊自動獲取,退出(正常/異常)自動釋放。
- 同步塊以原子方式執行,其他線程不可見中間狀態。
重入
- 粒度為「線程」而非「調用」。同一線程重複獲取同一鎖計數器 +1,退出 -1,歸零釋放。
- 簡化封裝:父子同步方法調用無需額外協調。
用鎖保護狀態
- 所有可變共享狀態由同一把鎖保護。
- 封裝性:將狀態私有化,通過對象內置鎖同步所有訪問路徑。
- 僅保護真正共享的可變數據,不可變/線程封閉數據無需鎖。
- 涉及多變量的不變性條件(如
lower <= upper),所有相關變量必須由同一把鎖保護。
活躍性與性能
- 同步塊不可過小(破壞原子性)也不可過大(降低併發度)。
- 長時間計算、I/O 操作(網絡/控制台)嚴禁在同步塊內執行。
- 將不影響共享狀態的耗時操作移出同步塊。