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

Java多線程通信機制深度解析:wait/notify、線程狀態與同步協作

     編輯:Java 相關知識

線程通信是併發編程核心難點,Java通過Object.wait/notify、LockSupport.park/unpark、Condition三套機制實現線程協作。本文系統梳理線程狀態流轉、通信模式與避坑指南。

線程六大狀態流轉


NEW (新建) 
  ↓ start()
RUNNABLE (可執行) ←→ BLOCKED (阻塞/鎖等待)
  ↓ wait()/join()/park()          ↓ 獲取鎖
WAITING (無限等待) / TIMED_WAITING (超時等待)
  ↓ notify()/notifyAll()/unpark()/超時
RUNNABLE (重新競爭CPU)
  ↓ run()結束/異常拋出
TERMINATED (終止)

Object監視器機制(synchronized配套)

核心規則

  • 前提:調用wait/notify必須持有對象監視器(在synchronized(obj)塊內)
  • wait():釋放鎖,進入WAITING,等待notify
  • notify():隨機喚醒一個等待該對象的線程
  • notifyAll():喚醒所有等待該對象的線程
  • 虛假喚醒:線程可能無notify自動醒來,必須在循環中檢查條件

標準生產者-消費者模板


class BoundedBuffer {
    private final List buffer = new ArrayList<>();
    private final int capacity;
    
    public BoundedBuffer(int capacity) { this.capacity = capacity; }
    
    public synchronized void put(T item) throws InterruptedException {
        while (buffer.size() == capacity) {  // 條件循環防虛假喚醒
            wait();
        }
        buffer.add(item);
        notifyAll();  // 喚醒消費者
    }
    
    public synchronized T take() throws InterruptedException {
        while (buffer.isEmpty()) {
            wait();
        }
        T item = buffer.remove(0);
        notifyAll();  // 喚醒生產者
        return item;
    }
}

Thread.sleep/yield/suspend對比

方法釋放鎖進入狀態喚醒方式用途
sleep(ms)否TIMED_WAITING時間到/中斷定時延遲
yield()否RUNNABLE調度器決定禮讓CPU(極少用)
wait()是WAITINGnotify/中斷條件等待
park()否WAITINGunpark/中斷底層阻塞原語
suspend()否-resume()已棄用(死鎖風險)

LockSupport底層原語(JUC基礎)


// 當前線程阻塞
LockSupport.park();           // 無限等待
LockSupport.parkNanos(nanos); // 定時等待
LockSupport.parkUntil(deadline); // 截止時間等待

// 喚醒指定線程
LockSupport.unpark(thread);   // 許可證機制,不阻塞

特點:不需要持有鎖、基於許可證(最多1個)、不拋InterruptedException(需自檢Thread.interrupted())

Condition精確控制(ReentrantLock配套)


class AdvancedBuffer {
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final Queue queue = new ArrayDeque<>();
    private final int capacity;
    
    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) notFull.await();
            queue.add(item);
            notEmpty.signal(); // 精確喚醒消費者
        } finally { lock.unlock(); }
    }
    
    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) notEmpty.await();
            T item = queue.poll();
            notFull.signal(); // 精確喚醒生產者
            return item;
        } finally { lock.unlock(); }
    }
}

優勢:一把鎖綁定多條件隊列、支持公平鎖、可中斷等待awaitUninterruptibly()、超時精確到納秒

經典死鎖場景與避免


// 錯誤:嵌套同步獲取鎖順序不一致
synchronized(lock1) { synchronized(lock2) { ... } }  // 線程1
synchronized(lock2) { synchronized(lock1) { ... } }  // 線程2 → 死鎖

// 正確:統一全局獲取順序
private static final Object LOCK1 = new Object(), LOCK2 = new Object();
synchronized(LOCK1) { synchronized(LOCK2) { ... } }  // 所有線程相同順序

// 或使用tryLock超時避免
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
    try { if (lock2.tryLock(1, TimeUnit.SECONDS)) { ... } }
    finally { lock1.unlock(); }
}

最佳實踐清單

  1. 優先使用高級併發工具:BlockingQueue、Semaphore、CountDownLatch、CompletableFuture
  2. wait/notify必在循環中:while(condition) wait();
  3. notifyAll優於notify:避免喚醒錯誤線程導致活鎖
  4. 釋放鎖前檢查狀態:確保條件滿足再notify
  5. 避免在同步塊調用外部方法:防止死鎖/活鎖/性能退化

掌握線程狀態機、監視器語義、Condition精確喚醒,是編寫正確高效併發代碼的前提。

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