synchronized 關鍵字是 Java 實現線程同步的核心機制。同步區塊可鎖定任意物件,粒度比同步方法更細,能有效減少競爭範圍。本文系統說明四種同步形式、監視器物件原理與實戰範例。
| 形式 | 鎖定物件 | 適用場景 |
|---|---|---|
| 實例方法同步 | this(當前實例) | 保護實例級共享資源 |
| 靜態方法同步 | 類別物件(ClassName.class) | 保護類別級共享資源 |
| 實例方法中的同步區塊 | 指定物件(常用 this 或私有鎖) | 僅同步方法部分程式碼 |
| 靜態方法中的同步區塊 | 指定物件(常用 ClassName.class) | 僅同步靜態方法部分程式碼 |
public class MyClass {
// 同步方法
public synchronized void log1(String msg1, String msg2) {
log.writeln(msg1); log.writeln(msg2);
}
// 同步區塊,鎖定 this
public void log2(String msg1, String msg2) {
synchronized(this) {
log.writeln(msg1); log.writeln(msg2);
}
}
}
兩者同步於同一實例,效果等同。
public class MyClass {
public static synchronized void log1(String msg1, String msg2) { ... }
public static void log2(String msg1, String msg2) {
synchronized(MyClass.class) { ... }
}
}
public class Counter {
long count = 0;
public synchronized void add(long value) { this.count += value; }
}
// 兩線程共用同一 Counter 實例 -> 串行執行
Counter counter = new Counter();
new Thread(() -> counter.add(1)).start();
new Thread(() -> counter.add(1)).start();
// 兩線程各用不同 Counter 實例 -> 並行執行
Counter counterA = new Counter(), counterB = new Counter();
new Thread(() -> counterA.add(1)).start();
new Thread(() -> counterB.add(1)).start();
class Goods {
private String name; private int num; private boolean flag = false;
public synchronized void produce(String name) {
while (flag) { try { wait(); } catch (InterruptedException e) {} }
this.name = name + "編號:" + num++;
System.out.println(Thread.currentThread().getName() + "生產:" + this.name);
flag = true; notifyAll();
}
public synchronized void consume() {
while (!flag) { try { wait(); } catch (InterruptedException e) {} }
System.out.println(Thread.currentThread().getName() + "消費:" + name);
flag = false; notifyAll();
}
}
synchronized(obj)),僅有一線程能進入同一監視器的同步區塊。this 或私有 final Object lock = new Object();類別資源用 ClassName.class。wait()/notify()/notifyAll() 必須在同步區塊內呼叫,且作用於同一監視器物件。while 而非 if 檢查條件,防止虛假喚醒。同步區塊提供比同步方法更細粒度的鎖控制,能顯著提升並發效能。掌握監視器物件選擇、條件等待與喚醒機制,是編寫正確高效多線程程式的基礎。