synchronized 是 Java 並發編程的基石,保證臨界區的原子性與可見性。但「鎖住的是誰」往往是新手最易踩的坑。本文通過層層遞進的反面教材,徹底講清鎖對象、this、類對象的區別,並給出死鎖預防原則。
class OutPuter {
public void outer(String name) {
synchronized (name) { // 危險!name 是參數,每次調用可能不同對象
for (char c : name.toCharArray()) System.out.print(c);
System.out.println();
}
}
}
// 線程 1 傳 "abcdefg",線程 2 傳 "123456789" → 兩把不同的鎖 → 交替輸出,線程不安全
class OutPuter {
public void outer(String name) {
synchronized (this) { // 同一個 OutPuter 實例 → 同一把鎖
for (char c : name.toCharArray()) System.out.print(c);
System.out.println();
}
}
}
// 或直接在方法上加 synchronized(隱式鎖 this)
public synchronized void outer(String name) { ... }
class OutPuter {
public synchronized void outer(String name) { ... } // 鎖 this
public synchronized void outer2(String name) { ... } // 也鎖 this
}
// 兩個方法互斥,因為鎖對象都是同一個 OutPuter 實例
// 線程 1
outPuter.outer("abcdefg");
// 線程 2 —— 重新 new 了一個對象!
new OutPuter().outer("123456789"); // 不同實例 → 不同鎖 → 線程不安全
class OutPuter {
public static synchronized void outer3(String name) { ... } // 鎖 OutPuter.class
public synchronized void outer(String name) { ... } // 鎖 this
}
// 靜態鎖(類鎖)與實例鎖(對象鎖)完全無關 → 不互斥
修正:實例方法改鎖類對象
public void outer(String name) {
synchronized (OutPuter.class) { ... } // 現在與 outer3 共享類鎖
}
| 同步方式 | 鎖對象 | 作用範圍 |
|---|---|---|
synchronized(instanceMethod) | 當前實例 this | 同一實例的所有實例同步方法互斥 |
synchronized(this) | 當前實例 this | 同上 |
synchronized(lockObj) | 自定義 Lock 對象 | 共享該 lockObj 的代碼塊互斥 |
static synchronized method | 當前類 Class 對象 | 所有實例的該靜態方法互斥 |
synchronized(MyClass.class) | 指定類 Class 對象 | 跨實例、跨方法的類級別互斥 |
ReentrantLock 支持 tryLock(timeout) 可中斷,CountDownLatch/Semaphore 替代 wait/notify。synchronized(this) 或方法級 synchronized。synchronized(ClassName.class)。private final Object lock = new Object();)。String、Integer、方法參數等可能被池化或替換的對象。