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

LockSupport 深度解析:Park/Unpark 機制、線程阻塞喚醒的底層藝術

     編輯:Java 綜合教學

前言

LockSupport 是 JUC(java.util.concurrent)包中最基礎的線程阻塞/喚醒原語。AQS、ReentrantLock、CountDownLatch、FutureTask 等高級組件底層均依賴它。本文深度剖析其核心 API、許可證機制、三大核心場景、源碼實現原理及與 Object.wait/notify 的本質區別。

一、核心 API 與許可證機制

1.1 方法速覽

方法作用關鍵特性
park()阻塞當前線程,等待許可證無參,可攜帶 blocker 對象用於診斷
park(Object blocker)阻塞並記錄阻塞器LockSupport.getBlocker(thread) 可獲取,排查死鎖利器
parkNanos(long nanos)限時阻塞(相對時間)超時自動返回,不拋異常
parkUntil(long deadline)限時阻塞(絕對時間)截止時間 = System.nanoTime() + timeout
unpark(Thread thread)發放許可證,喚醒目標線程可提前發放,許可證不累積(最多 1 個)
getBlocker(Thread thread)獲取線程阻塞時傳入的 blocker需線程調用 park(blocker) 才生效

1.2 許可證機制:一個「不累積」的信號量

  • 每個線程關聯一個許可證(初始 0)。
  • unpark:許可證 = 1(若已為 1 則保持 1,不疊加)。
  • park:若許可證 = 1 → 消耗許可證(歸 0)立即返回;若許可證 = 0 → 線程阻塞,等待許可證。
  • 關鍵推論: unpark 可在 park 之前調用(提前發放),線程隨後調用 park 直接通過不阻塞。這解決了 wait/notify 的「通知丟失」痛點。

二、三大經典使用場景實戰

2.1 場景一:子線程等待主線程通知

public static void main(String[] args) throws InterruptedException {
    Thread worker = new Thread(() -> {
        System.out.println("Worker: 開始工作,等待許可...");
        LockSupport.park(); // 阻塞
        System.out.println("Worker: 獲得許可,繼續執行");
    });
    worker.start();

    Thread.sleep(1000); // 模擬主線程準備
    System.out.println("Main: 發放許可");
    LockSupport.unpark(worker); // 喚醒
}
/* 輸出:
Worker: 開始工作,等待許可...
Main: 發放許可
Worker: 獲得許可,繼續執行
*/

2.2 場景二:主線程提前發放許可(解決競態)

public static void main(String[] args) {
    Thread worker = new Thread(() -> {
        try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
        System.out.println("Worker: 準備就緒,檢查許可...");
        LockSupport.park(); // 直接通過,不阻塞!
        System.out.println("Worker: 繼續執行");
    });
    worker.start();

    System.out.println("Main: 提前發放許可");
    LockSupport.unpark(worker); // 許可證 = 1
}
/* 輸出:
Main: 提前發放許可
Worker: 準備就緒,檢查許可...
Worker: 繼續執行
*/

2.3 場景三:傳遞阻塞上下文信息(診斷神器)

public static void main(String[] args) throws InterruptedException {
    Thread worker = new Thread(() -> {
        String context = "任務ID: 10086, 優先級: HIGH";
        System.out.println("Worker: 阻塞,上下文: " + context);
        LockSupport.park(context); // 傳遞 blocker
        System.out.println("Worker: 喚醒");
    });
    worker.start();

    Thread.sleep(500);
    System.out.println("Main: 檢查阻塞器 -> " + LockSupport.getBlocker(worker)); // 獲取上下文
    LockSupport.unpark(worker);
}
/* 輸出:
Worker: 阻塞,上下文: 任務ID: 10086, 優先級: HIGH
Main: 檢查阻塞器 -> 任務ID: 10086, 優先級: HIGH
Worker: 喚醒
*/

三、源碼級原理剖析

3.1 核心字段:UNSAFE 與 ParkEvent

// LockSupport 靜態內部類
static class ParkEvent {
    volatile int state; // 0=無許可, 1=有許可
    // ... 平台相關 mutex/cond
}

// 關鍵 UNSAFE 實例
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();

3.2 unpark 實現:釋放許可 + 唤醒

public static void unpark(Thread thread) {
    if (thread != null) {
        ParkEvent ev = getParkEvent(thread); // 獲取/創建線程關聯的 ParkEvent
        ev.state = 1; // 設置許可證 = 1
        ev.release(); // 平台相關:pthread_cond_signal / SetEvent / unpark
    }
}

3.3 park 實現:檢查許可 → 消耗或阻塞

public static void park() {
    ParkEvent ev = getParkEvent(Thread.currentThread());
    if (ev.state == 1) { // 快速路徑:有許可
        ev.state = 0;    // 消耗許可
        return;
    }
    // 慢速路徑:無許可,進入阻塞
    ev.park(); // 平台相關:pthread_cond_wait / WaitForSingleObject / park
    ev.state = 0; // 被喚醒後消耗許可
}

四、LockSupport vs Object.wait/notify:本質區別

維度LockSupport.park/unparkObject.wait/notify
鎖依賴無需鎖必須持有對象監視器鎖
通知丟失不丟失(許可證機制)易丟失(notify 前 wait 未執行)
喚醒目標精確指定線程隨機喚醒一個/全部
攜帶上下文支持 blocker 診斷不支持
超時控制納秒/截止時間精確毫秒精度
中斷響應阻塞中可中斷(Thread.interrupt)可中斷,拋 InterruptedException
適用層級底層原語,構建鎖/同步器高層協作,業務代碼慎用

五、進階:中斷與虛假喚醒處理

public void awaitWithInterrupt() throws InterruptedException {
    while (!conditionMet) { // 防虛假喚醒/中斷喚醒
        LockSupport.park(this);
        if (Thread.interrupted()) { // 檢查中斷狀態,不拋異常
            throw new InterruptedException();
        }
    }
}
// 或使用 parkNanos/parkUntil 實現超時等待

六、總結

LockSupport 以許可證機制解決了傳統 wait/notify 的「通知丟失、隨機喚醒、鎖綁定」三大痛點,成為 JUC 同步體系的基石原語。掌握其無鎖阻塞、提前發放、攜帶上下文、精確喚醒特性,不僅能讀懂 AQS/ReentrantLock 源碼,更能在需要精細線程控制的場景(如自定義同步器、線程池任務協調、無鎖算法輔助)中靈活運用,寫出更底層、更高效的並發代碼。

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