在多線程環境下,對象如何安全共享是並發編程的核心課題。本文系統梳理可見性、發布與逸出、線程封閉、不可變對象及安全發布模式,構建完整知識體系。
缺乏同步時,編譯器、運行時可能對操作重排序,導致線程讀取到失效數據。
線程讀取共享變量時,可能看到舊值,而非其他線程最新寫入的值。
Java 內存模型保證基本類型讀寫原子性,但 非 volatile 的 long/double (64位) 允許拆分為兩次 32 位操作。多線程並發讀寫時,可能讀到「高 32 位來自線程 A,低 32 位來自線程 B」的混合值。解決:聲明 volatile 或加鎖保護。
鎖不僅提供互斥,還建立 happens-before 關係:解鎖 happens-before 後續加鎖。所有訪問共享變量的線程必須在同一把鎖上同步,才能保證可見性。
對比:鎖 = 可見性 + 原子性;volatile = 僅可見性
發布:使對象在作用域外可見(如存入 public static 字段)。
逸出:不該發布的對象被發布(如構造過程中 this 引用外洩)。
start()/initialize() 啟動線程不共享數據,自然線程安全。三種實現層次:
靠程序員自覺維護,脆弱。適用於單線程子系統(如 Swing 事件分發線程、請求處理線程)。
局部變量天然封閉在線程棧中。將非線程安全對象作為局部變量使用,即可實現線程安全。
為每個線程提供獨立副本。get() 返回當前線程最近 set() 的值。常用於:資料庫連接持有、請求上下文傳遞、格式化器復用。
警示:類似全局變量,降低代碼復用性,引入隱式耦合,謹慎使用。
創建後狀態不可修改,天然線程安全。滿足三條件:
finalprivate final將多個相關變量封裝為一個不可變對象,更新時創建新實例。讀取線程始終看到一致狀態,無需加鎖。
發布對象時,必須確保引用與狀態同時對其他線程可見。
volatile 域或 AtomicReferencefinal 域Hashtable/ConcurrentMap/SynchronizedMap:put 操作安全發布鍵值Vector/CopyOnWriteArrayList/SynchronizedList:add 操作安全發布元素BlockingQueue/ConcurrentLinkedQueue:offer 操作安全發布元素| 對象類型 | 發布要求 |
|---|---|
| 不可變對象 | 任意方式發布均安全 |
| 事實不可變對象 | 必須安全發布 |
| 可變對象 | 安全發布 + 線程安全或鎖保護 |