核心原理:synchronized 鎖的是「對象監視器」
- 實例方法:鎖
this(當前對象)。
- 靜態方法:鎖
ClassName.class(類對象)。
- 同步代碼塊:鎖
synchronized(obj) 指定的對象。
同一把鎖(同一對象監視器)同一時刻只能被一個線程持有。不同鎖互不影響。
實戰驗證:類鎖 vs 對象鎖互不干擾
public class Printer {
// 靜態同步方法 → 鎖 Printer.class
public static synchronized void printA() {
log("printA start");
sleep(1000);
log("printA end");
}
// 實例同步方法 → 鎖 this
public synchronized void printB() {
log("printB start");
sleep(1000);
log("printB end");
}
// 普通方法 → 無鎖
public void printC() { log("printC"); }
private void log(String msg) {
System.out.println(Thread.currentThread().getName() + " " + msg);
}
private void sleep(int ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
}
測試場景 1:兩線程分別調用 printA (靜態) 和 printB (實例)
Thread-A: printA start
Thread-A: printA end
Thread-B: printB start
Thread-B: printB end
// 或交替輸出,關鍵是:printA 和 printB 可並發執行,因為鎖對象不同(Printer.class vs this)
測試場景 2:同一實例並發調用 printB
Thread-1: printB start
Thread-1: printB end
Thread-2: printB start // 必須等待 Thread-1 釋放 this 鎖
Thread-2: printB end
測試場景 3:printC (無鎖) 與 printB 並發
Thread-1: printC
Thread-2: printB start
Thread-2: printB end
Thread-1: printC
// printC 不受 printB 鎖影響,隨時可執行
同步代碼塊靈活控制鎖粒度
public void syncBlockDemo(Object lock) {
System.out.println("非同步代碼..."); // 無鎖
synchronized (lock) { // 僅鎖住關鍵區
// 臨界區操作
}
System.out.println("非同步代碼..."); // 無鎖
}
常見併發陷阱與避坑
| 陷阱 | 現象 | 修正 |
| 鎖字符串常量 | synchronized("lock") 常量池共享,非預期全局互斥 | 用 new Object() 或 this / Class.class |
| 鎖基本類型包裝對象 | synchronized(Integer.valueOf(1)) 緩存池導致不同實例共用鎖 | 用 new Integer(1) 或專用鎖對象 |
| 同步方法中調用異步/耗時操作 | 長時間持有鎖,吞吐率暴跌 | 縮小同步塊範圍,異步操作移出同步塊 |
| 死鎖 | 線程 A 鎖 1 等 2,線程 B 鎖 2 等 1 | 固定加鎖順序、tryLock 超時、避免嵌套鎖 |
JVM 優化:偏向鎖 → 輕量級鎖 → 重量級鎖
- JDK 1.6+ 引入鎖升級機制:無鎖 → 偏向鎖(單線程重複獲取) → 輕量級鎖(CAS,低併發) → 重量級鎖(OS 互斥量,高併發)。
synchronized 性能已接近 ReentrantLock,優先選擇 synchronized(代碼簡潔、JVM 原生支持、逃逸分析可消除鎖)。