Java 並發編程:線程間通信 wait/notify 生產者消費者模式
通過共享資源對象實現線程協作,演示 synchronized + wait()/notify() 經典生產者-消費者模式,解決線程安全與高效協作問題。
核心組件設計
- Resource:共享數據載體 (
name、sex)
- Input (生產者):循環寫入中文/拼音數據
- Output (消費者):循環讀取並打印
- 鎖對象:唯一的
Resource 實例
完整代碼實現
class Resource {
String name;
String sex;
}
class Input implements Runnable {
private final Resource resource;
public Input(Resource resource) { this.resource = resource; }
@Override
public void run() {
boolean toggle = true;
while (true) {
synchronized (resource) { // 獲取鎖
if (toggle) {
resource.name = "陶士涵";
resource.sex = "男";
} else {
resource.name = "taoshihan";
resource.sex = "nan";
}
toggle = !toggle;
resource.notify(); // 喚醒消費者
try {
resource.wait(); // 釋放鎖並等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
class Output implements Runnable {
private final Resource resource;
public Output(Resource resource) { this.resource = resource; }
@Override
public void run() {
while (true) {
synchronized (resource) {
System.out.println(resource.name + "=====" + resource.sex);
resource.notify(); // 喚醒生產者
try {
resource.wait(); // 釋放鎖並等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
public class WaitNotifyDemo {
public static void main(String[] args) {
Resource resource = new Resource(); // 唯一共享對象
new Thread(new Input(resource), "生產者").start();
new Thread(new Output(resource), "消費者").start();
}
}
運行效果
陶士涵=====男
taoshihan=====nan
陶士涵=====男
taoshihan=====nan
... 交替輸出,無數據錯亂
關鍵點深度解析
1. 同步前提:同一把鎖
- 生產者、消費者
synchronized(resource) 使用同一實例
- 若鎖不一致,
wait/notify 會拋 IllegalMonitorStateException
2. wait/notify 語義
| 方法 | 釋放鎖 | 線程狀態 | 喚醒方式 |
wait() | 是 | WAITING | notify()/notifyAll() |
sleep() | 否 | TIMED_WAITING | 時間到期自動 |
3. 經典協作流程
- 生產者獲取鎖 → 生產數據 →
notify() 喚醒消費者 → wait() 釋放鎖等待
- 消費者獲取鎖 → 消費數據 →
notify() 喚醒生產者 → wait() 釋放鎖等待
- 循環往複,實現嚴格交替
4. 死鎖風險
- 嵌套同步塊且鎖順序不一致
- 忘記
notify() 導致永久等待
- 使用
notifyAll() 替代 notify() 可減少信號丟失風險 (多消費者場景)
現代替代方案 (JUC)
生產環境推薦使用 java.util.concurrent 組件,更安全、功能更豐富:
| 場景 | 推薦類 | 優勢 |
| 有界隊列生產消費 | ArrayBlockingQueue | 內置阻塞、無需手動 wait/notify |
| 延遲任務 | DelayQueue | 按時間排序 |
| 線程協作 | CountDownLatch/CyclicBarrier/Phaser | 語義清晰、可復用 |
| 鎖替代 | ReentrantLock + Condition | 多條件隊列、可中斷、公平鎖 |
BlockingQueue 版生產消費者 (推薦)
BlockingQueue queue = new ArrayBlockingQueue<>(10);
new Thread(() -> {
while (true) {
queue.put("data-" + System.currentTimeMillis()); // 滿則阻塞
}
}, "Producer").start();
new Thread(() -> {
while (true) {
System.out.println("Consumed: " + queue.take()); // 空則阻塞
}
}, "Consumer").start();
- 上一篇文章: Spring MVC 快速整合 Apache Shiro 實戰指南
- 下一篇文章: Java I/O 進階:對象序列化、轉換流與隨機訪問文件 RandomAccessFile 詳解
Copyright ©
程式碼之家 All Rights Reserved