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

Java 多執行緒協作經典:兩執行緒交替列印 AB、三執行緒循環列印 ABC

     編輯:Java 綜合教學

問題定義

  • 執行緒 A 僅列印 A,執行緒 B 僅列印 B,各 10 次,輸出 ABABAB...。
  • 擴展:執行緒 A/B/C 分別列印 A/B/C,各 100 次,輸出 ABCABC...。

核心考點:等待/通知機制(wait()/notify()/notifyAll())、共享狀態協調、避免虛假喚醒(用 while 而非 if)。

方案一:雙執行緒交替列印 AB(布爾旗標法)

public class PrintAB {
    public static void main(String[] args) {
        final Printer printer = new Printer();
        new Thread(() -> { for (int i = 0; i < 10; i++) printer.printA(); }, "Thread-A").start();
        new Thread(() -> { for (int i = 0; i < 10; i++) printer.printB(); }, "Thread-B").start();
    }
}

class Printer {
    private boolean flag = true; // true 輪到 A,false 輪到 B

    public synchronized void printA() {
        while (!flag) { // 虛假喚醒防護
            try { this.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        }
        System.out.print("A");
        flag = false;
        this.notify(); // 喚醒 B
    }

    public synchronized void printB() {
        while (flag) {
            try { this.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        }
        System.out.print("B");
        flag = true;
        this.notify(); // 喚醒 A
    }
}

關鍵點

  1. synchronized 確保檢查旗標與修改旗標的原子性。
  2. while (!flag) 防止虛假喚醒(JVM 允許無通知自動喚醒)。
  3. notify() 單喚醒足夠,因只有另一個執行緒在等待。

方案二:三執行緒循環列印 ABC(狀態機法)

class PrinterABC {
    private int state = 0; // 0:A, 1:B, 2:C

    public synchronized void printA() {
        while (state != 0) { wait(); }
        System.out.print("A");
        state = 1;
        notifyAll(); // 必須喚醒所有,因 B/C 皆可能在等
    }
    public synchronized void printB() {
        while (state != 1) { wait(); }
        System.out.print("B");
        state = 2;
        notifyAll();
    }
    public synchronized void printC() {
        while (state != 2) { wait(); }
        System.out.print("C");
        state = 0;
        notifyAll();
    }
}

為何用 notifyAll()?

三執行緒時,喚醒單一執行緒可能喚醒「非目標」執行緒(如 A 執行完喚醒 C,C 發現 state!=1 繼續等待,導致 B 永遠不被喚醒)。notifyAll() 確保正確執行緒最終被喚醒。

進階方案:JUC 顯式鎖 + Condition(推薦生產環境)

class JucPrinter {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition aCon = lock.newCondition();
    private final Condition bCon = lock.newCondition();
    private final Condition cCon = lock.newCondition();
    private int state = 0;

    public void printA() {
        lock.lock();
        try {
            while (state != 0) aCon.await();
            System.out.print("A");
            state = 1;
            bCon.signal();
        } finally { lock.unlock(); }
    }
    // printB/printC 類似,signal 目標 Condition
}

優勢:精準喚醒(signal() 對應特定 Condition)、可中斷等待(awaitInterruptibly())、公平鎖選項、多條件變數語義清晰。

擴展:N 執行緒循環列印通用模板

class CyclicPrinter {
    private final int n; // 執行緒數
    private int current = 0;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition[] conditions;

    public CyclicPrinter(int n) {
        this.n = n;
        conditions = new Condition[n];
        for (int i = 0; i < n; i++) conditions[i] = lock.newCondition();
    }

    public void print(int id, String text, int times) {
        for (int i = 0; i < times; i++) {
            lock.lock();
            try {
                while (current != id) conditions[id].await();
                System.out.print(text);
                current = (current + 1) % n;
                conditions[current].signal();
            } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
            finally { lock.unlock(); }
        }
    }
}

面試加分點總結

  • 能寫出 while + wait/notify 正確模板。
  • 能說清 notify vs notifyAll 適用場景。
  • 能對比 synchronized 與 ReentrantLock+Condition 優劣,推薦 JUC 寫法。
  • 能延伸到「生產者消費者」、「順序執行任務編排」等實戰模型。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved