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

Java synchronized 關鍵字實戰:同步方法與塊的線程安全演示

     編輯:Java 相關知識

前言

synchronized 是 Java 並發編程的基石關鍵字,基於對象監視器實現互斥同步。本文通過銀行賬戶轉賬模擬,演示同步方法、靜態同步與非靜態同步的鎖對象差異,並總結 synchronized 與 Lock 的選型建議。

一、核心原理

  • 每個 Java 對象/類關聯一把內部鎖:實例方法鎖 this、靜態方法鎖 Class 對象、同步塊鎖指定對象。
  • 線程進入同步區需獲取鎖,退出(正常/異常)自動釋放,JVM 保證原子性。
  • 可重入:同一線程可重複獲取同一鎖(計數器機制)。

二、範例一:實例同步方法保護共享賬戶

2.1 代碼結構

  • Account:余額 balance,synchronized addAmount/subtractAmount。
  • Company/Bank 實現 Runnable,分別並發 100 次存/取 1000。
  • 主線程 join() 等待完成,驗證最終餘額等於初始值。

2.2 關鍵代碼

class Account {
    private double balance;
    public synchronized void addAmount(double amount) {
        double tmp = balance;
        Thread.sleep(10); // 放大並發窗口
        tmp += amount;
        balance = tmp;
    }
    public synchronized void subtractAmount(double amount) { ... }
}

2.3 運行結果

Account : Initial Balance: 1000.000000
Account : Final Balance: 1000.000000  // 始終一致

去掉 synchronized:最終餘額隨機偏離 1000,多次運行結果不一,證明數據競態。

2.4 鎖行為驗證

  • 同一 Account 實例的兩個同步方法互斥:線程 A 執行 addAmount 時,線程 B 嘗試 subtractAmount 會阻塞。
  • 不同 Account 實例互不干擾:各自鎖各自的 this。

三、範例二:靜態同步 vs 實例同步 —— 鎖對象不同可並發

3.1 修改點

class Account {
    private static double balance = 0; // 靜態共享
    public static synchronized void addAmount(double amount) { ... } // 鎖 Account.class
    public synchronized void subtractAmount(double amount) { ... }   // 鎖 this
}

3.2 現象

最終餘額不再固定,每次運行結果不同。
原因:addAmount 鎖 Account.class,subtractAmount 鎖具體實例 this,兩把鎖互不相干,線程可同時進入,發生競態。

3.3 修正方案

  • 統一用靜態同步方法(鎖 Class)。
  • 或統一用實例同步方法(鎖同一實例)。
  • 或同步塊顯式指定同一鎖對象:synchronized(Account.class) { ... }。

四、synchronized 進階特性總結

特性說明對比 Lock
實現層級JVM 內部(監視器進入/退出字節碼)JDK API (AQS)
公平性非公平,新線程可搶佔,防饑餓可選公平/非公平
條件等待單一 wait/notify,不靈活Condition 支持多條件隊列
中斷響應不可中斷,只能等待獲取鎖lockInterruptibly() 可響應中斷
性能JDK 6+ 鎖優化後極具競爭力高並發下可優於未優化 synchronized

五、最佳實踐建議

  1. 優先 synchronized:代碼簡潔、JVM 優化成熟(偏向鎖、輕量級鎖、鎖消除、鎖粗化),除非需要 Condition/中斷/公平鎖。
  2. 縮小鎖範圍:同步塊優於同步方法,僅保護共享狀態讀寫區。
  3. 避免鎖活躍對象:不要鎖 String/包裝類/集合等可能被複用的對象,專用 private final Object lock = new Object();。
  4. 死鎖預防:固定多鎖獲取順序、使用 tryLock 超時、避免持有鎖調用外部方法。

掌握 synchronized 的鎖對象語義與 JVM 優化機制,是寫出高性能線程安全代碼的前提。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved