synchronized 鎖對象的選擇直接決定同步範圍。錯誤選擇(如 String 常量、Boxed 基本類型)會導致非預期的跨實例同步,嚴重影響併發性能甚至死鎖。
public class SyncTest {
private String name = "name"; // 字面量進入常量池
public void method(String flag) {
synchronized (name) { // 所有實例共用同一把鎖!
System.out.println(flag + ", invoke method ....");
Thread.sleep(1000);
}
}
public static void main(String[] args) {
SyncTest t1 = new SyncTest(), t2 = new SyncTest();
// 4 條線程分別綁定 t1、t1、t2、t2
// 預期:t1 兩線程同步,t2 兩線程同步,t1/t2 互不干擾
// 實際:4 線程全串行,因鎖定同一常量池 "name"
}
}
public class SyncTest {
private final byte[] lock = new byte[0]; // 每個實例唯一、不可變、零內存
public void method(String flag) {
synchronized (lock) { // 實例級鎖,互不干擾
System.out.println(flag + ", invoke method f....");
Thread.sleep(1000);
}
}
// main 測試同前,輸出符合預期:t1 兩線程串行,t2 兩線程串行,t1/t2 並行
}
new byte[0]?| 候選對象 | 問題 | 推薦度 |
|---|---|---|
| String 字面量 | 常量池共享,跨實例誤同步 | ❌ |
| new String() | 每次 new 創建新對象,若非 final 字段可能被重新賦值導致鎖失效 | ⚠️ |
| Integer/Long 等包裝類 | -128~127 緩存導致共享;大數值 new 出新對象同 String 問題 | ❌ |
| this | 暴露鎖給外部調用者,破壞封裝,易死鎖 | ⚠️ |
| private final Object lock = new Object() | 標準做法,每實例 ~16 字節頭部開銷 | ✅ |
| private final byte[] lock = new byte[0] | 零數組無數據區,僅對象頭 ~16 字節,語義明確「僅作鎖」 | ⭐ 最佳 |
需可中斷、公平、多條件變量時,優先 java.util.concurrent.locks.ReentrantLock:
private final Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try { /* critical section */ }
finally { lock.unlock(); }
}
private final byte[] lock = new byte[0],零內存、語義清晰。Lock 體系。