Stories

Detail Return Return

併發編程:Java常用線程調度方法及實現原理 - Stories Detail

本文將介紹Java常用線程調度方法及實現原理,包括sleep、wait¬ify、join、park&unpark。

線程方法

方法 説明
start() 用於啓動線程,讓線程進入就緒狀態 ;
RUNNABLE 多次調用拋 IllegalThreadStateException 異常
run() 線程運行時會調用 run() 方法執行具體邏輯
join() 當前線程由運行狀態轉為等待狀態
getId() 獲取線程ID
getName()/setName() 獲取/設置線程名
getPriority()/setPriority() 獲取/設置優先級,優先級 1~10,默認 5,優先級僅起提示作用
getState() 獲取線程狀態
interrupt() 打斷線程
isInterrupted() 判斷是否被打斷,不會清除打斷標記
static interrupted() 判斷是否被打斷,會清除打斷標記
isAlive() 是否存活,即是否未運行完畢
static currentThread() 獲取當前正在運行的線程
static sleep() 線程由運行狀態轉為等待狀態;
TimeUnit 的 sleep() 比 Thread 的 sleep() 更具可讀性
static yield() 線程由運行狀態轉為就緒狀態

對於 interrupt()

  • 處於等待狀態(sleep、wait、join)的線程被 interrupt() 後 ,會拋異常 InterruptedException,打斷標記為false(被清除);
  • 處於等待狀態(park)的線程被 interrupt() 後,打斷標記為 true。park() 僅對打斷標記為 false 的線程生效;
  • 處於運行狀態的線程被 interrupt() 後,線程仍正常執行,打斷標記為 true

Thread - sleep

正在運行的線程主動調用 Thread.sleep(long) 方法,由運行狀態轉為等待狀態。

public class Thread implements Runnable {
    public static void sleep(long millis) throws InterruptedException {
        if (millis < 0) {...}
        long nanos = MILLISECONDS.toNanos(millis);
        ThreadSleepEvent event = beforeSleep(nanos);
        try {
            if (currentThread() instanceof VirtualThread vthread) {
                // 虛擬線程
                vthread.sleepNanos(nanos);
            } else {
                // 最終調用本地方法 sleep0()
                sleep0(nanos);
            }
        } finally {
            afterSleep(event);
        }
    }
    private static native void sleep0 (long nanos) throws InterruptedException;    
}

object - wait & notify

正在運行的線程發現條件不滿足時,主動調用 obj.wait(),進入 obj 對象對應的 Monitor 對象的 WaitSet 等待,狀態轉為 WAITING。

當其他線程調用 notify()/notifyAll() 方法時,會喚醒 WaitSet 中的一個/全部線程,使其進入 EntryList 參與鎖的競爭。

public class Object {
    public final void wait(long timeoutMillis) throws InterruptedException {
        long comp = Blocker.begin();
        try {
            // 最終調用本地方法 wait0()
            wait0(timeoutMillis);
        } catch (InterruptedException e) {...} finally {...}
    }
    private final native void wait0(long timeoutMillis) throws InterruptedException;
}

thread - join

join() 體現的是保護性暫停模式(Guarded Suspension),join() 通過 wait() 實現等待。

保護性暫停模式:線程執行任務時,若不滿足條件則轉為等待狀態(保護、暫停),直到滿足條件。為防止虛假喚醒,線程每次被喚醒需要再次檢查條件是否滿足。

public class Thread implements Runnable {
    // join(long) 的需要滿足的條件就是超過等待時間或者被等待線程結束
    public final void join(long millis) throws InterruptedException {
        if (millis < 0){...};
        if (this instanceof VirtualThread vthread) {..}
        // 核心代碼
        synchronized (this) {
            if (millis > 0) {
                if (isAlive()) {
                    final long startTime = System.nanoTime();
                    long delay = millis;
                    do {
                        // 等待
                        wait(delay);
                    } while (
                        // 被等待線程存活
                        isAlive() && 
                        // 等待時間未超過限制
                         (
                             // 計算當前線程繼續等待時間
                             delay = millis - 
                                 NANOSECONDS.toMillis(
                                     System.nanoTime() - startTime
                                 )
                         ) > 0
                            );
                }
            } else {
                // 無時限等待,直到被等待線程結束
                while (isAlive()) {
                    wait(0);
                }
            }
        }
    }
}

LockSupport - park & unpark

每個線程都有自己的一個 Parker 對象,由三部分組成 _counter , _cond 和 _mutex 。

調用 LockSupport.park(),檢查 _counter,若為 0,則進入 _cond 條件變量等待,若為 1,則獲得互斥鎖 _mutex ,將 _counter 置為0。

調用 LockSupport.unpark(),檢查 _counter,將 _counter 置為1,喚醒條件變量 \_cond 中等待的線程。

  • park() 和 unpark() 是 LockSupport 類的靜態方法,不需要和 Monitor 搭配使用
  • unpark 可以在 park 之前執行

LockSupport 的 park() / unpark() 基於 Unsafe 的 park() / unpark(),Unsafe 提供了較為底層的操作內存、線程的方法。

public class LockSupport {
    public static void park() {
        if (Thread.currentThread().isVirtual()) {
            // 虛擬線程
            VirtualThreads.park();
        } else {
            // 調用Unsafe的park()
            U.park(false, 0L);
        }
    }
    private static final Unsafe U = Unsafe.getUnsafe();
}

方法辨析

sleep & wait

  • sleep() 是 Thread 的靜態方法,wait() 是 Object 的方法;
  • sleep() 不需要和 synchronized 搭配使用,wait() 需要和 synchronized 搭配使用;
  • sleep() 不會釋放鎖對象,wait() 會釋放鎖對象。

wait & park

  • notify() 必須在 wait() 之後執行,unpark() 可在 park() 之前執行;
  • wait() 需要和 synchronized 搭配使用,park() 不需要和 synchronized 搭配使用;
  • notify() / notifyAll() 只能喚醒隨機一個或者全部等待線程;unpark() 可以精確喚醒特定線程。

END

文章文檔:公眾號 字節幺零二四 回覆關鍵字可獲取本文文檔。

如果覺得本文對您有一點點幫助,歡迎點贊轉發,這會對我有非常大的幫助,咱們下期見!

user avatar king_wenzhinan Avatar journey_64224c9377fd5 Avatar sofastack Avatar u_16502039 Avatar u_13529088 Avatar jump_and_jump Avatar aipaobudezuoyeben Avatar xialeistudio Avatar nianqingyouweidenangua Avatar chang_lehung Avatar cqu_jiangzhou Avatar xiongshihubao Avatar
Favorites 37 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.