synchronized 是 Java 中解決並發問題最基礎、最常用的同步機制。它不僅能確保線程互斥訪問同步代碼,還能保證共享變量的內存可見性,並有效解決指令重排序問題。本文將從基本用法、底層原理、實戰案例三個維度深度解析。
public synchronized void instanceMethod() {
// 同步代碼塊
// 鎖對象:當前實例 (this)
}
鎖住的是當前對象實例 this。同一實例的不同同步方法互斥,不同實例互不影響。
public static synchronized void staticMethod() {
// 鎖對象:當前類的 Class 對象
}
鎖住的是類級別的 Class 對象。所有實例共享同一把鎖,屬於類級別同步。
public void blockSync() {
synchronized (this) { // 或自定義鎖對象
// 精確控制同步範圍
}
}
靈活指定鎖對象,可縮小同步範圍,提升並發性能。
public class SyncDemo {
public void method1() { /* 耗時 3秒 */ }
public void method2() { /* 耗時 1秒 */ }
public static void main(String[] args) {
SyncDemo demo = new SyncDemo();
new Thread(() -> demo.method1()).start();
new Thread(() -> demo.method2()).start();
}
}
輸出:Method 1 start → Method 1 execute → Method 2 start → Method 2 execute → Method 2 end → Method 1 end
分析:兩線程並發執行,互不阻塞。
public synchronized void method1() { ... }
public synchronized void method2() { ... }
輸出:Method 1 完全結束後,Method 2 才開始
分析:同一實例的兩個同步方法共享 this 鎖,互斥執行。
public static synchronized void method1() { ... }
public static synchronized void method2() { ... }
// 主方法中創建兩個不同實例 test 和 test2
輸出:仍然串行執行
分析:靜態方法鎖的是 SyncDemo.class,與實例無關。
public void method1() {
System.out.println("Method 1 start");
synchronized (this) {
System.out.println("Method 1 execute");
Thread.sleep(3000);
}
System.out.println("Method 1 end");
}
輸出:Method 1 start → Method 1 execute → Method 2 start → Method 1 end → Method 2 execute → Method 2 end
分析:非同步代碼可並發,只有進入同步塊時才阻塞。
通過 javap -c 反編譯查看字節碼:
monitorenter // 進入監視器
// 同步代碼
monitorexit // 退出監視器
ACC_SYNCHRONIZED 標誌位
// 無 monitorenter/monitorexit,由 JVM 通過方法標誌隱式實現
這就是為什麼 wait()/notify() 必須在同步塊中調用的原因——它們依賴 Monitor 對象。
| 案例 | 鎖對象 | 執行模式 | 核心原因 |
|---|---|---|---|
| 實例方法同步 | 同一實例 (this) | 串行 | 競爭同一把對象鎖 |
| 靜態方法同步 | Class 對象 | 串行 | 不同實例共享類鎖 |
| 同步代碼塊 | 括號內對象 | 部分串行 | 僅同步塊互斥,外部並發 |
ReentrantLock、volatile、Atomic 類、無鎖算法深入理解 Synchronized 的 Monitor 機制,不僅能幫助正確使用同步關鍵字,更能從根本上理解 Java 並發編程的核心模型,為選擇最優並發策略奠定基礎。