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

Java 線程交替執行與共享變量操作:wait/notify 經典實現

     編輯:Java 相關知識

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);
    }
}

關鍵點解析

  1. while 而非 if:防止虛假喚醒,必須在循環中檢查條件。
  2. 同步鎖同一對象:兩方法均用 synchronized 實例方法,鎖住 this。
  3. notifyAll vs notify:此處兩線程用 notifyAll 更保險;單一等待線程可用 notify。
  4. 標誌位 volatile:此處標誌位在同步塊內讀寫,無需 volatile;若無同步則必加。

案例二:四線程對共享整數增減(兩增兩減)

需求:共享變量 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();
    }
}

進階:使用 AtomicInteger 無鎖實現

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

常見錯誤避坑

  1. wait/notify 未在同步塊中 → 拋 IllegalMonitorStateException。
  2. 用 if 替代 while 檢查條件 → 虛假喚醒導致邏輯錯誤。
  3. notify 喚醒錯誤線程 → 多等待線程時用 notifyAll 或 Condition。
  4. 共享變量未 volatile/同步 → 可見性問題,線程讀到舊值。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved