synchronized 背後是物件監視器。**多執行緒必須競爭同一個物件實例的鎖**,才能實現互斥。若每個執行緒鎖住自己的 this,根本無法同步。
class MyThread implements Runnable {
private int id;
public MyThread(int id) { this.id = id; }
@Override
public synchronized void run() { // 鎖住 this (每個實例不同)
for (int i = 0; i < 100; i++)
System.out.println("Thread " + id + " : " + i);
}
}
public class Demo {
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
new Thread(new MyThread(i)).start(); // 10 把不同的鎖,完全無同步效果
}
}
結果:10 執行緒輸出交錯亂序。
class MyThread implements Runnable {
private int id;
private final Object lock; // 共享鎖
public MyThread(int id, Object lock) { this.id = id; this.lock = lock; }
@Override
public void run() {
synchronized (lock) { // 競爭同一把鎖
for (int i = 0; i < 100; i++)
System.out.println("Thread " + id + " : " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
Object sharedLock = new Object(); // 唯一共享鎖
for (int i = 0; i < 10; i++)
new Thread(new MyThread(i, sharedLock)).start();
}
}
結果:執行緒 0 印完 0~99,執行緒 1 再印 0~99,依序進行。
class MyThread implements Runnable {
private int id;
private static final Object LOCK = new Object(); // 類別級唯一鎖
public MyThread(int id) { this.id = id; }
@Override
public void run() {
synchronized (LOCK) { ... }
}
}
無需建構子傳遞,所有實例天然共享 MyThread.LOCK。
class MyThread implements Runnable {
private int id;
public MyThread(int id) { this.id = id; }
@Override
public void run() {
taskHandler(id); // 呼叫靜態同步方法
}
private static synchronized void taskHandler(int id) { // 鎖住 MyThread.class
for (int i = 0; i < 100; i++)
System.out.println("Thread " + id + " : " + i);
}
}
static synchronized 等同 synchronized (MyThread.class),同樣達到全域互斥。
| 寫法 | 鎖物件 | 共享範圍 | 適用場景 |
|---|---|---|---|
synchronized void instanceMethod() | this | 同一實例 | 實例級資料保護 |
synchronized (sharedObj) | 外部傳入/靜態欄位 | 所有持有相同引用的執行緒 | 跨實例協作、精細鎖粒度 |
static synchronized void staticMethod() | Xxx.class | 所有實例、類別級 | 靜態資料、單例模式、類別級資源 |
synchronized("lock") 看似共享,實則可能因常數池優化導致不同類別載入器載入的相同字串不是同一物件,極度危險。synchronized (Integer.valueOf(1)) 同理,自動拆箱/裝箱可能產生新物件。final Object lock = new Object(); 明確語意,防止誤覆寫引用。private,避免外部代碼刻意/誤用同一把鎖造成活鎖或死鎖。private static final Lock LOCK = new ReentrantLock();
public void run() {
LOCK.lock();
try { ... } finally { LOCK.unlock(); }
}
提供可中斷、逾時、公平鎖等進階控制,同樣需靜態共享實例。
同步的本質是**多執行緒圍繞同一個物件鎖排隊**。寫法千變萬化,核心只有一條:**確保參與同步的所有執行緒,看到的是同一個鎖物件實例**。