synchronized 是 Java 並發編程的基石,本質是監視器鎖。它保證三大特性:原子性、可見性、有序性。JDK 1.6 之後經過大量優化(偏向鎖、輕量級鎖、鎖消除、鎖粗化、自旋鎖、適應性自旋),性能已非早期「重量級」可比,應優先考慮使用,而非盲目追求 `Lock` 或無鎖結構。
// 實例方法:鎖為 this
public synchronized void instanceMethod() { ... }
// 靜態方法:鎖為 Class 對象
public static synchronized void staticMethod() { ... }
等價於:
public void instanceMethod() {
synchronized(this) { ... }
}
public static void staticMethod() {
synchronized(MyClass.class) { ... }
}
// 任意對象作為鎖
synchronized(lockObject) {
// 臨界區代碼
}
靈活性更高:可精確控制鎖範圍(縮小臨界區提高併發度)、指定任意鎖對象(實現多把鎖分離)。
| 代碼形式 | 鎖對象 | 作用範圍 |
|---|---|---|
| synchronized 實例方法 | 當前實例 (this) | 同一實例的所有同步方法互斥 |
| synchronized 靜態方法 | 當前類 Class 對象 | 所有實例的該靜態方法互斥 |
| synchronized(this) | 當前實例 (this) | 同一實例的該塊代碼互斥 |
| synchronized(ClassName.class) | Class 對象 | 所有實例的該塊代碼互斥 |
| synchronized(privateLock) | 自定義私有對象 | 僅控制訪問該對象的代碼塊 |
synchronized 的語義由 JMM (Java Memory Model) 定義:
無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖
(單線程) (低併發) (高併發)
| 鎖類型 | 適用場景 | 核心機制 | 優缺點 |
|---|---|---|---|
| 偏向鎖 | 無併發、單線程重複獲取 | CAS 設置 ThreadID 到 Mark Word,後續進出無 CAS | 極低開銷;有併發時撤銷成本高 |
| 輕量級鎖 | 低併發、交替執行 | CAS 將鎖記錄指針存入 Mark Word,自旋等待 | 無阻塞;自旋消耗 CPU |
| 重量級鎖 | 高併發、長耗時臨界區 | OS 互斥量,線程阻塞掛起 | 公平調度;上下文切換開銷大 |
// ✅ 好:私有 final 對象,不可被外部竄改
private final Object lock = new Object();
// ✅ 好:零長度 byte 數組,創建開銷最小(3 字節碼 vs Object 7 字節碼)
private final byte[] lock = new byte[0];
// ❌ 坏:String 常量池導致不同類共享同一把鎖
synchronized("lock") { ... }
// ❌ 坏:基本類型包裝器自動拆箱裝箱導致鎖對象變化
Integer count = 0;
synchronized(count) { count++; } // 每次鎖住新對象!
// ❌ 坏:this 可能被外部代碼 synchronized,導致死鎖風險
// 建議:私有鎖封裝內部狀態,公開鎖僅用於協作
// ❌ 粗粒度:整個方法加鎖,包含 IO、睡眠
public synchronized void process() {
saveToDb(); // 耗時操作
sendEmail();
}
// ✅ 細粒度:僅保護共享狀態讀寫
public void process() {
Data snapshot;
synchronized(lock) { snapshot = copyData(); } // 快速複製
saveToDb(snapshot); // 耗時操作移出鎖
sendEmail(snapshot);
}
不要手寫 `synchronizedMap`,直接用:
| 維度 | synchronized | ReentrantLock |
|---|---|---|
| API 層級 | 關鍵字/JVM 內置 | 類庫級 (AQS) |
| 公平性 | 非公平 | 可選公平/非公平 |
| 條件變量 | wait/notify/notifyAll | 多個 Condition |
| 可中斷 | 不可中斷 | lockInterruptibly() |
| 超時獲取 | 不支持 | tryLock(timeout) |
| 性能 (高併發) | 優化後極具競爭力 | 略優/持平 |
| 調試支持 | JVM 內置監控 | 需代碼埋點 |
建議:優先用 `synchronized`(代碼簡潔、JVM 優化紅利、工具支持好);需公平鎖、條件變量、可中斷、超時獲取等高級特性時,再用 `ReentrantLock`。
`synchronized` 看似簡單,實則蘊含「鎖對象選擇、內存語義、JVM 優化機制、工程避坑」四大維度深度。掌握私有 final 鎖對象、臨界區最小化、理解鎖升級與消除、善用線程安全集合,是寫出高性能、無 Bug 並發代碼的關鍵。記住:過早優化是萬惡之源,先正確,再快速,最後才是併發優化。