首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 線程協作經典範例:wait/notify 實現交替打印

     編輯:Java 程式設計入門

問題描述

兩個線程交替打印 1-100:線程 A 打印 1-10 → 通知線程 B → 線程 B 打印 1-10 → 通知線程 A → 線程 A 打印 11-20... 循環直至 100。

核心機制:對象鎖與等待隊列

  • 每個 Java 對象關聯一把內部鎖和等待隊列
  • synchronized 獲取鎖,執行同步代碼
  • wait():釋放鎖,進入等待隊列
  • notify():喚醒隊列中一個線程
  • notifyAll():喚醒所有等待線程

完整實現代碼

package jdeveloper.study;

public class DemoThread implements Runnable {
    private int current = 1;
    private final int MAX = 100;
    private final int STEP = 10;
    private boolean thread1Turn = true;

    public static void main(String[] args) {
        DemoThread demo = new DemoThread();
        new Thread(demo, "Thread-1").start();
        new Thread(demo, "Thread-2").start();
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        
        while (true) {
            synchronized (this) {
                // 檢查是否輪到當前線程
                boolean myTurn = thread1Turn && name.equals("Thread-1") 
                                || !thread1Turn && name.equals("Thread-2");
                
                if (!myTurn) {
                    try {
                        wait();  // 釋放鎖並等待
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                    continue;  // 被喚醒後重新檢查條件
                }

                // 執行打印任務
                for (int i = 0; i < STEP && current <= MAX; i++) {
                    System.out.println(name + ": " + current++);
                }

                // 切換輪次並通知另一線程
                thread1Turn = !thread1Turn;
                notify();  // 喚醒等待線程
                
                if (current > MAX) {
                    notifyAll();  // 確保另一線程也能退出
                    return;
                }
            }
        }
    }
}

關鍵設計要點

1. 共享狀態封裝

使用共享對象 this 作為鎖,封裝 current、thread1Turn 等協作狀態。

2. 等待條件循環檢查


while (!myTurn) {
    wait();
}

防止虛假喚醒,確保條件滿足才繼續執行。

3. 原子性操作

打印邏輯、狀態更新、通知操作在同一 synchronized 塊中,保證原子性。

4. 終止條件處理

當 current > MAX 時調用 notifyAll() 確保另一線程從 wait() 返回並檢查退出條件。

常見錯誤與避坑指南

錯誤寫法問題正確做法
在非同步塊調用 wait/notify拋出 IllegalMonitorStateException必須在 synchronized(this) 內調用
使用 if 而非 while 檢查條件虛假喚醒導致邏輯錯誤始終使用 while 循環檢查
notify 後繼續持有鎖執行耗時操作被喚醒線程無法獲取鎖notify 應為同步塊最後操作
兩線程使用不同鎖對象無法協作共享同一鎖對象

擴展思考

  • 多線程輪轉:使用 turn = (turn + 1) % N 擴展到 N 個線程
  • 生產者-消費者模式:本質相同,緩衝隊列替換計數器
  • 現代替代方案:Lock + Condition、Phaser、CompletableFuture 提供更高級抽象

掌握 wait/notify 機制是理解 Java 併發基石,雖然實際開發中推薦使用高級併發工具,但底層原理通用且面試必考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved