Java 多線程協作核心在於 wait()/notify() 機制。本文通過兩個經典案例 —— 主線程/子線程交替打印 與 多線程對共享變量增減 —— 詳細演示同步、等待通知、標誌位控制的正確用法。
需求:主線程打印 10 次 → 子線程打印 10 次 → 循環 50 輪。
public class AlternatePrint {
private boolean mainTurn = true; // true: 主線程執行, false: 子線程執行
public synchronized void mainPrint(int round) {
while (!mainTurn) {
try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; }
}
for (int i = 0; i < 10; i++) {
System.out.println("Main: round=" + round + ", i=" + i);
}
mainTurn = false;
notifyAll(); // 喚醒子線程
}
public synchronized void subPrint(int round) {
while (mainTurn) {
try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; }
}
for (int i = 0; i < 10; i++) {
System.out.println("Sub : round=" + round + ", i=" + i);
}
mainTurn = true;
notifyAll(); // 喚醒主線程
}
public static void main(String[] args) {
AlternatePrint ap = new AlternatePrint();
new Thread(() -> {
for (int r = 0; r < 50; r++) ap.subPrint(r);
}, "SubThread").start();
for (int r = 0; r < 50; r++) ap.mainPrint(r);
}
}
synchronized 實例方法,鎖住 this。notifyAll 更保險;單一等待線程可用 notify。需求:共享變量 j,兩線程各加 1 五次,兩線程各減 1 五次,輸出操作過程。
public class SharedCounter {
private static int j = 0;
private final Object lock = new Object(); // 顯式鎖對象
public void increment() {
synchronized (lock) {
j++;
System.out.println(Thread.currentThread().getName() + " +1 -> j=" + j);
}
}
public void decrement() {
synchronized (lock) {
j--;
System.out.println(Thread.currentThread().getName() + " -1 -> j=" + j);
}
}
public static void main(String[] args) {
SharedCounter sc = new SharedCounter();
Runnable inc = () -> { for (int k = 0; k < 5; k++) sc.increment(); };
Runnable dec = () -> { for (int k = 0; k < 5; k++) sc.decrement(); };
new Thread(inc, "Inc-1").start();
new Thread(inc, "Inc-2").start();
new Thread(dec, "Dec-1").start();
new Thread(dec, "Dec-2").start();
}
}
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private final AtomicInteger j = new AtomicInteger(0);
public void increment() {
int val = j.incrementAndGet();
System.out.println(Thread.currentThread().getName() + " +1 -> j=" + val);
}
public void decrement() {
int val = j.decrementAndGet();
System.out.println(Thread.currentThread().getName() + " -1 -> j=" + val);
}
// main 同前
}
優勢:無鎖、性能更高、代碼更簡潔,適合簡單計數器場景。
| 場景 | 推薦方案 | 關鍵點 |
|---|---|---|
| 線程嚴格交替/協作 | wait/notify + 標誌位 | while 循環檢查、同一把鎖、notifyAll |
| 共享計數器 | AtomicInteger/LongAdder | 無鎖、高性能、線程安全 |
| 複雜狀態機 | Lock + Condition | 多條件隊列、可中斷、公平鎖 |
| 生產者消費者 | BlockingQueue | 內建阻塞、無需手動 wait/notify |
IllegalMonitorStateException。notifyAll 或 Condition。