許多開發者知道 synchronized 依賴對象的隱式鎖,但對「靜態方法」與「實例方法」同步時的鎖對象差異往往存在誤區。本文通過一個經典案例徹底釐清這一核心概念。
public class ImplicitLock {
public static synchronized void method1() {
System.out.println("method1 executing...");
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
}
public synchronized void method2() {
System.out.println("method2 executing...");
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
public class ImplicitLockSample {
public static void main(String[] args) {
final ImplicitLock sample = new ImplicitLock();
new Thread(() -> sample.method1()).start(); // 呼叫靜態方法
new Thread(() -> sample.method2()).start(); // 呼叫實例方法
}
}
兩個線程幾乎**同時**打印輸出,**沒有** 3 秒的等待間隔。原因在於它們持有的是**兩把完全不同的鎖**:
method1() 是 static,鎖對象是 ImplicitLock.class(Class 對象)。method2() 是實例方法,鎖對象是 sample 這個實例(this)。Class 繼承自 Object,擁有隱式鎖。this當前類.classThread.sleep() 僅讓出 CPU,不釋放已持有的監視器鎖。若將 method1() 改為 ImplicitLock.method1() 顯式調用,或給 method2() 加上 static,兩者將競爭同一把鎖,此時第二個線程會阻塞約 3 秒。
這個例子源於對「懶漢式單例」中 static synchronized 的重新審視。許多時候我們「誤打誤撞」寫出線程安全代碼,卻不理解底層鎖機制。**搞清楚鎖到底鎖住了誰,是並發編程的基石。**