Java 中每個對象有且僅有一把同步鎖。調用對象的 synchronized 方法或進入 synchronized 代碼塊即獲取該鎖。鎖訪問互斥:同一時刻只能被一個線程持有,實現多線程對對象/方法的互斥訪問。
線程訪問對象的 synchronized 方法/塊時,其他線程訪問該對象的同一方法/塊將被阻塞。
// Demo1_1: 共享 Runnable 實例 -> 共享鎖 -> 串行執行
class MyRunable implements Runnable {
public void run() {
synchronized(this) { ... }
}
}
public class Demo1_1 {
public static void main(String[] args) {
Runnable demo = new MyRunable();
new Thread(demo, "t1").start();
new Thread(demo, "t2").start(); // t1, t2 共享 demo 實例鎖
}
}
結果:t1 執行完 5 次循環後,t2 才開始。
線程訪問同步塊時,其他線程仍可訪問該對象的非同步代碼。
// Demo2: t1 調用 synMethod(同步),t2 調用 nonSynMethod(非同步) -> 並發執行
class Count {
public void synMethod() { synchronized(this) { ... } }
public void nonSynMethod() { ... } // 無 synchronized
}
結果:t1 和 t2 交替輸出。
線程訪問對象的某同步方法/塊時,其他線程訪問該對象的其他同步方法/塊也會被阻塞。
// Demo3: 將 nonSynMethod 也加上 synchronized(this) -> 串行執行
public synchronized void foo() { ... },鎖是 this(實例方法)或類對象(靜態方法)。synchronized(obj) { ... },可精確控制鎖的範圍與粒度,通常效率更高。// Demo4: 性能對比
public synchronized void synMethod() { for(int i=0;i<1000000;i++); }
public void synBlock() { synchronized(this) { for(int i=0;i<1000000;i++); } }
// 結果:synBlock() 耗時通常少於 synMethod()
| 鎖類型 | 對應形式 | 作用範圍 |
|---|---|---|
| 實例鎖 | synchronized 修飾實例方法/塊 | 鎖定具體實例對象,單例時等同全局鎖 |
| 全局鎖 | static synchronized 或鎖 .class | 針對類,所有實例共享 |
class Something {
public synchronized void isSyncA() {}
public synchronized void isSyncB() {}
public static synchronized void cSyncA() {}
public static synchronized void cSyncB() {}
}
Something x = new Something(), y = new Something();
x.isSyncA() vs x.isSyncB():不能並發(同一實例鎖)。x.isSyncA() vs y.isSyncA():可並發(不同實例鎖)。x.cSyncA() vs y.cSyncB():不能並發(共享類鎖)。x.isSyncA() vs Something.cSyncA():可並發(實例鎖 vs 類鎖)。每種情況均附有完整代碼與運行結果驗證。