synchronized 是 Java 並發編程中最基礎、最核心的同步手段。本文將系統梳理其鎖機制、作用域、死鎖風險、JVM 內存模型(JMM)交互、以及與 ThreadLocal、ReentrantLock 的對比選型。
關鍵點:synchronized 鎖的是對象,而非代碼塊或方法。
class MyClass {
synchronized void myMethod() { /* ... */ }
}
MyClass obj = new MyClass();
obj.myMethod(); // 線程需獲取 obj 實例的鎖
線程執行同步方法/塊前,必須先獲得該對象的監視器鎖(Monitor Lock)。
| 修飾位置 | 鎖對象 | 作用範圍 |
|---|---|---|
| 實例方法 | 當前實例 this | 同一實例的所有同步方法互斥 |
| 靜態方法 | 類對象 MyClass.class | 所有實例共享,類級別互斥 |
同步代碼塊 synchronized(obj) | 括號內對象 obj | 靈活控制鎖粒度 |
synchronized。
// 經典死鎖:鎖順序相反
void methodA() { synchronized(lock1) { synchronized(lock2) { ... } } }
void methodB() { synchronized(lock2) { synchronized(lock1) { ... } } }
wait() 釋放鎖並阻塞線程;notify() 隨機喚醒一個等待線程(需重新獲取鎖才能繼續)。
核心規則:
synchronized 不一定有 wait/notify。wait/notify 必有 synchronized:因兩者操作的是對象鎖,調用時必須持有該鎖,否則拋 IllegalMonitorStateException。
public class Bank {
float[] accounts;
public synchronized void transfer(int from, int to, float amount) {
while (accounts[from] < amount) {
wait(); // 余額不足,釋放鎖等待
}
accounts[from] -= amount;
accounts[to] += amount;
notifyAll(); // 喚醒所有等待線程重新檢查條件
}
}
最佳實踐:
while 而非 if(防止虛假喚醒)。notifyAll() 避免信號丟失。InterruptedException 後應恢復中斷狀態:Thread.currentThread().interrupt()。JMM 定義:
線程操作變量三步驟:讀取主存→工作內存計算→寫回主存。JVM 不保證步驟即時執行,導致可見性問題。
synchronized 的語義保證:
這強制實現了 原子性、可見性、有序性。
本質區別:
| 維度 | synchronized | ThreadLocal |
|---|---|---|
| 目的 | 多線程共享變量同步 | 線程間數據隔離,各自副本 |
| 機制 | 鎖、內存屏障 | Map<Thread, T> 存儲副本 |
| 性能 | 競爭激烈時低 | 無鎖,極高 |
| 適用場景 | 必須共享狀態 | 線程獨享上下文(如 DB 連接、Session) |
| 缺陷 | ReentrantLock 改進 |
|---|---|
| 單一 Condition | 支持多個 Condition(如 notEmpty/notFull) |
| 不可中斷等待 | lockInterruptibly() 可響應中斷 |
| 無超時獲取 | tryLock(timeout) 支持超時 |
| 公平性無法控制 | 構造函數指定公平/非公平鎖 |