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

Java 線程通信深度解析:從 wait/notify 到 Lock/Condition 的進化之路

     編輯:Java 綜合教學

Java 線程通信深度解析:從 wait/notify 到 Lock/Condition 的進化之路

多線程環境下,線程間如何協作處理共享資源是並發編程的核心課題。本文將從傳統的 wait/notify 機制講起,逐步揭示其在多生產者多消費者場景下的缺陷,並引入 JDK 1.5 引入的 Lock 和 Condition 作為現代解決方案。

一、傳統等待喚醒機制

核心方法

  • wait():釋放鎖並進入等待狀態,線程被存入等待隊列
  • notify():隨機喚醒一個等待線程
  • notifyAll():喚醒所有等待線程

關鍵規則:這些方法必須在同步塊或同步方法中調用,因為它們操作的是當前對象的監視器鎖。

為什麼定義在 Object 類中?

因為監視器本質上就是鎖,任意對象都可作為鎖,所以監視器方法必須定義在所有對象的父類 Object 中。

二、經典生產者-消費者模型

class Resource {
    private String name;
    private int count = 1;
    private boolean flag = false;

    public synchronized void set(String name) {
        if (flag) { 
            try { this.wait(); } catch (InterruptedException e) {}
        }
        this.name = name + count++;
        System.out.println(Thread.currentThread().getName() + " 生產: " + this.name);
        flag = true;
        notify();
    }

    public synchronized void out() {
        if (!flag) { 
            try { this.wait(); } catch (InterruptedException e) {}
        }
        System.out.println(Thread.currentThread().getName() + " 消費: " + this.name);
        flag = false;
        notify();
    }
}

單生產者單消費者時運行完美,但擴展到多生產者多消費者會出現問題。

三、多生產者多消費者的陷阱與解決

問題 1:if 判斷導致虛假喚醒

當多個生產線程被 wait() 後,喚醒時不再檢查條件直接執行,導致重複生產。

解決:將 if 改為 while 循環檢查條件。

問題 2:while + notify 導致死鎖

喚醒的是同類線程(如喚醒另一個生產者),雙方都在等待,無線程能推進業務。

解決:使用 notifyAll() 確保至少喚醒一個對方線程。

總結

  • if:只檢查一次,不安全
  • while:循環檢查,解決虛假喚醒
  • notifyAll:喚醒所有,解決死鎖
  • 多生產多消費必須用 while + notifyAll

四、Lock/Condition:精準控制的現代方案

JDK 1.5 java.util.concurrent.locks 包提供了更靈活的鎖機制:

核心改進

  • Lock 替代 synchronized:顯式獲取/釋放鎖,支持中斷、超時、公平鎖
  • Condition 替代 Object 監視器:一個 Lock 可創建多個 Condition,實現精準喚醒

代碼實現

class Resource1 {
    private String name;
    private int count = 1;
    private boolean flag = false;
    private Lock lock = new ReentrantLock();
    private Condition producerCon = lock.newCondition();
    private Condition consumerCon = lock.newCondition();

    public void set(String name) {
        lock.lock();
        try {
            while (flag) producerCon.await();
            this.name = name + count++;
            System.out.println(Thread.currentThread().getName() + " 生產: " + this.name);
            flag = true;
            consumerCon.signal(); // 精準喚醒消費者
        } finally {
            lock.unlock();
        }
    }

    public void out() {
        lock.lock();
        try {
            while (!flag) consumerCon.await();
            System.out.println(Thread.currentThread().getName() + " 消費: " + this.name);
            flag = false;
            producerCon.signal(); // 精準喚醒生產者
        } finally {
            lock.unlock();
        }
    }
}

對應關係

傳統機制Lock/Condition
synchronizedLock.lock() / unlock()
wait()Condition.await()
notify()Condition.signal()
notifyAll()Condition.signalAll()

五、實戰案例:有界緩衝區

class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();
    final Object[] items = new Object[100];
    int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) notFull.await();
            items[putptr] = x;
            if (++putptr == items.length) putptr = 0;
            ++count;
            notEmpty.signal();
        } finally { lock.unlock(); }
    }

    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) notEmpty.await();
            Object x = items[takeptr];
            if (++takeptr == items.length) takeptr = 0;
            --count;
            notFull.signal();
            return x;
        } finally { lock.unlock(); }
    }
}

總結

Lock/Condition 將鎖和監視器解耦,支持多條件變量,實現了「生產者喚醒消費者、消費者喚醒生產者」的精準控制,徹底解決了傳統機制的效率與死鎖問題,是高並發場景下的首選方案。

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