synchronized 是 Java 實現多線程同步的核心關鍵字,看似簡單卻暗藏玄機。本文系統梳理其作用於不同位置時的鎖對象差異、同步方法與同步塊的等價關係,以及避免死鎖、減少開銷的最佳實踐。
synchronized 鎖定的是對象,而非代碼塊或方法本身。public synchronized void methodAAA() { ... }
// 等價於
public void methodAAA() {
synchronized (this) { ... }
}
鎖對象為調用該方法的實例對象(如 P1)。同一實例的多線程調用會互斥,但不同實例(P2)互不干擾,可能導致數據競態。
public void method3(SomeObject so) {
synchronized (so) { ... }
}
鎖為參數 so 指向的對象。若需同步一段無明確對象的代碼,可創建專用鎖對象:
private byte[] lock = new byte[0]; // 零長度 byte[] 開銷最小(3 條字節碼 vs Object 7 條)
synchronized (lock) { ... }
public synchronized static void methodAAA() { ... }
// 等價於
public void methodBBB() {
synchronized (Foo.class) { ... }
}
鎖為類對象(Class Literal),即 Foo.class。注意 P1.getClass() 若為子類則不可用作同類鎖。
直接使用 synchronized (Foo.class),效果同靜態同步方法,鎖定當前類的 Class 對象。
同一類中,synchronized static 方法與 synchronized instance 方法鎖不同(Class vs 實例),兩者並發執行不會互斥,若共享靜態/實例變量需額外協調。
private 實例變量 + getter,避免 public/protected 變量繞過同步控制。getter 應同步並返回 clone() 副本,防止外部修改內部狀態。搞清「鎖是誰」是編寫正確多線程程序的前提。合理選擇同步粒度、縮小同步塊範圍、優先使用 java.util.concurrent 高級工具,才能在保證線程安全的同時兼顧性能。