最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?Thread.join()方法使用詳細(xì)解析

 更新時(shí)間:2026年01月12日 09:00:10   作者:碼靈  
Thread.join()是Java多線程編程中的關(guān)鍵方法,用于確保線程執(zhí)行順序和數(shù)據(jù)完整性,下面這篇文章主要介紹了Java?Thread.join()方法使用的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

全面介紹Java 中Thread.join()方法的用法,包括其核心作用、重載方法、工作原理、使用場景及注意事項(xiàng),從基礎(chǔ)到進(jìn)階逐步拆解,結(jié)合可運(yùn)行示例幫你徹底理解。

一、核心定位

Thread.join() 是 Thread 類的實(shí)例方法,核心作用是實(shí)現(xiàn)線程間的同步(等待機(jī)制):當(dāng)一個(gè)線程(如主線程)調(diào)用另一個(gè)目標(biāo)線程(如子線程)的join()方法時(shí),調(diào)用線程會(huì)進(jìn)入阻塞狀態(tài),直到目標(biāo)線程執(zhí)行完畢(進(jìn)入 TERMINATED 狀態(tài))或等待超時(shí),才會(huì)繼續(xù)執(zhí)行自身后續(xù)代碼。

簡單說:A.join() 讓「當(dāng)前線程」等「線程 A」執(zhí)行完,再繼續(xù)自己的工作。

二、重載方法

Java 提供了 3 個(gè)重載的join()方法,滿足不同等待需求:

方法簽名功能說明
void join() throws InterruptedException無參重載:無限等待,調(diào)用線程會(huì)一直阻塞,直到目標(biāo)線程完全執(zhí)行終止(不會(huì)主動(dòng)超時(shí))
void join(long millis) throws InterruptedException帶毫秒?yún)?shù):超時(shí)等待,調(diào)用線程最多阻塞millis毫秒(千分之一秒),超時(shí)后自動(dòng)喚醒繼續(xù)執(zhí)行
void join(long millis, int nanos) throws InterruptedException帶毫秒 + 納秒?yún)?shù):高精度超時(shí)等待,理論上支持納秒級精度,但實(shí)際受操作系統(tǒng)時(shí)鐘精度限制(一般無需使用,優(yōu)先用毫秒重載)

關(guān)鍵說明

  1. join(0) 等價(jià)于 join():表示「無限等待目標(biāo)線程終止」,并非等待 0 毫秒。
  2. 納秒?yún)?shù)(nanos)取值范圍:0-999999,超出該范圍會(huì)拋出IllegalArgumentException。
  3. 所有重載方法均會(huì)拋出InterruptedException(受檢異常),必須捕獲或聲明拋出(因?yàn)榈却械木€程可能被其他線程中斷)。

三、工作原理

1. 核心邏輯

當(dāng)線程T1調(diào)用線程T2.join()時(shí),底層執(zhí)行流程如下:

  1. T1(調(diào)用線程)會(huì)先判斷T2(目標(biāo)線程)是否還處于存活狀態(tài)(isAlive());
  2. T2仍存活,T1會(huì)調(diào)用Object.wait()方法(底層依賴 Object 的等待機(jī)制),進(jìn)入阻塞狀態(tài);
  3. 當(dāng)T2執(zhí)行完畢終止時(shí),JVM 會(huì)自動(dòng)調(diào)用T2notifyAll()方法,喚醒所有等待在T2對象上的線程(即T1);
  4. 若設(shè)置了超時(shí)時(shí)間,T1在等待超時(shí)后會(huì)自動(dòng)喚醒,無需T2終止。

2. 與Thread.sleep()的核心區(qū)別

很多人會(huì)混淆join()sleep(),二者的核心差異在于鎖釋放使用場景

特性Thread.join()Thread.sleep(long millis)
方法類型實(shí)例方法(針對目標(biāo)線程)靜態(tài)方法(針對當(dāng)前線程)
鎖處理阻塞時(shí)會(huì)釋放當(dāng)前線程持有的對象鎖阻塞時(shí)不釋放任何鎖資源
等待狀態(tài)無參:WAITING;帶參:TIMED_WAITINGTIMED_WAITING
喚醒條件目標(biāo)線程終止 / 等待超時(shí) / 被中斷時(shí)間到達(dá) / 被中斷
核心場景線程間同步(等待其他線程完成)當(dāng)前線程暫停指定時(shí)間

四、實(shí)戰(zhàn)示例

示例 1:無參join()- 主線程等待子線程完全執(zhí)行

public class JoinBasicDemo {
    public static void main(String[] args) {
        // 定義子線程:模擬耗時(shí)任務(wù)(打印1-5,每次間隔500ms)
        Thread subThread = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    Thread.sleep(500); // 模擬任務(wù)耗時(shí)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子線程執(zhí)行中:i = " + i);
            }
            System.out.println("子線程執(zhí)行完畢!");
        }, "子線程-Test");

        System.out.println("主線程啟動(dòng)子線程...");
        subThread.start(); // 啟動(dòng)子線程

        try {
            System.out.println("主線程開始等待子線程執(zhí)行完畢...");
            subThread.join(); // 主線程阻塞,等待subThread執(zhí)行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 子線程執(zhí)行完后,主線程才會(huì)執(zhí)行這里
        System.out.println("主線程繼續(xù)執(zhí)行,程序結(jié)束!");
    }
}

運(yùn)行結(jié)果(順序固定)

plaintext

主線程啟動(dòng)子線程...
主線程開始等待子線程執(zhí)行完畢...
子線程執(zhí)行中:i = 1
子線程執(zhí)行中:i = 2
子線程執(zhí)行中:i = 3
子線程執(zhí)行中:i = 4
子線程執(zhí)行中:i = 5
子線程執(zhí)行完畢!
主線程繼續(xù)執(zhí)行,程序結(jié)束!

示例 2:帶超時(shí)join(long millis)- 主線程等待超時(shí)后繼續(xù)執(zhí)行

修改示例 1 中的join()調(diào)用,設(shè)置超時(shí)時(shí)間 1500ms(子線程總耗時(shí) 2500ms):

public class JoinTimeoutDemo {
    public static void main(String[] args) {
        Thread subThread = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子線程執(zhí)行中:i = " + i);
            }
            System.out.println("子線程執(zhí)行完畢!");
        }, "子線程-Timeout");

        System.out.println("主線程啟動(dòng)子線程...");
        subThread.start();

        try {
            System.out.println("主線程開始等待子線程(超時(shí)1500ms)...");
            subThread.join(1500); // 最多等待1500ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 1500ms后,主線程不再等待,直接執(zhí)行后續(xù)代碼
        System.out.println("主線程等待超時(shí),繼續(xù)執(zhí)行自身邏輯!");
    }
}

運(yùn)行結(jié)果(主線程超時(shí)后提前執(zhí)行)

plaintext

主線程啟動(dòng)子線程...
主線程開始等待子線程(超時(shí)1500ms)...
子線程執(zhí)行中:i = 1
子線程執(zhí)行中:i = 2
子線程執(zhí)行中:i = 3
主線程等待超時(shí),繼續(xù)執(zhí)行自身邏輯!
子線程執(zhí)行中:i = 4
子線程執(zhí)行中:i = 5
子線程執(zhí)行完畢!

示例 3:多個(gè)線程join()- 串行等待 vs 并行等待

場景 1:串行等待(主線程依次等待多個(gè)子線程,子線程串行執(zhí)行)

public class JoinSerialDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程t1執(zhí)行中:i = " + i);
            }
        }, "線程t1");

        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程t2執(zhí)行中:i = " + i);
            }
        }, "線程t2");

        t1.start();
        t1.join(); // 主線程先等t1執(zhí)行完
        t2.start();
        t2.join(); // 再等t2執(zhí)行完

        System.out.println("所有子線程執(zhí)行完畢,主線程結(jié)束!");
    }
}

場景 2:并行等待(主線程同時(shí)等待多個(gè)已啟動(dòng)的子線程,子線程并行執(zhí)行)

public class JoinParallelDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程t1執(zhí)行中:i = " + i);
            }
        }, "線程t1");

        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程t2執(zhí)行中:i = " + i);
            }
        }, "線程t2");

        // 先同時(shí)啟動(dòng)兩個(gè)子線程
        t1.start();
        t2.start();

        // 主線程同時(shí)等待兩個(gè)子線程(誰先執(zhí)行完誰先喚醒,最終等待耗時(shí)為較慢線程的執(zhí)行時(shí)間)
        t1.join();
        t2.join();

        System.out.println("所有子線程執(zhí)行完畢,主線程結(jié)束!");
    }
}

五、關(guān)鍵注意事項(xiàng)(避坑指南)

  1. 調(diào)用對象必須是已啟動(dòng)的線程:若對未啟動(dòng)(NEW 狀態(tài))的線程調(diào)用join(),方法會(huì)直接返回,不會(huì)產(chǎn)生阻塞(因?yàn)?code>isAlive()返回 false,無需等待)。
    Thread t = new Thread(() -> {});
    t.join(); // 無阻塞,直接執(zhí)行后續(xù)代碼(t未啟動(dòng))
    
  2. 異常必須處理join()拋出的InterruptedException是受檢異常,若不捕獲 / 聲明拋出,編譯報(bào)錯(cuò)。當(dāng)調(diào)用線程被中斷時(shí),會(huì)觸發(fā)該異常并清除中斷狀態(tài)。
  3. join()不改變線程的啟動(dòng)狀態(tài)join()僅負(fù)責(zé)阻塞調(diào)用線程,不會(huì)自動(dòng)啟動(dòng)目標(biāo)線程,必須先調(diào)用targetThread.start(),再調(diào)用targetThread.join()
  4. 多個(gè)線程等待同一個(gè)目標(biāo)線程:若多個(gè)線程同時(shí)調(diào)用同一個(gè)目標(biāo)線程的join()方法,當(dāng)目標(biāo)線程終止時(shí),所有等待線程會(huì)被 JVM 同時(shí)喚醒(底層notifyAll())。
  5. 與鎖結(jié)合時(shí)的釋放特性:若調(diào)用線程持有某個(gè)對象鎖,調(diào)用join()后會(huì)釋放該鎖(因?yàn)榈讓诱{(diào)用wait()),而sleep()不會(huì)釋放鎖,這是并發(fā)編程中的關(guān)鍵差異。

六、底層源碼簡化解析(幫助理解)

以下是Thread.join()的核心源碼(JDK8),剔除了冗余邏輯,保留核心流程:

// 無參join() 等價(jià)于 join(0)
public final void join() throws InterruptedException {
    join(0);
}

// 帶毫秒?yún)?shù)的核心實(shí)現(xiàn)
public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    // millis=0 表示無限等待
    if (millis == 0) {
        // 只要目標(biāo)線程還存活,調(diào)用線程就一直wait
        while (isAlive()) {
            wait(0);
        }
    } else {
        // 超時(shí)等待邏輯:循環(huán)判斷,避免虛假喚醒
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

源碼關(guān)鍵點(diǎn)

  1. join()synchronized方法:鎖對象是目標(biāo)線程實(shí)例(保證線程安全)。
  2. 循環(huán)判斷isAlive():避免「虛假喚醒」(即使被意外喚醒,若目標(biāo)線程未終止,仍會(huì)繼續(xù)等待)。
  3. 依賴Object.wait():底層是 Object 的等待 / 喚醒機(jī)制,JVM 在目標(biāo)線程終止時(shí)自動(dòng)調(diào)用notifyAll()。

七、總結(jié)

  1. 核心功能Thread.join()實(shí)現(xiàn)線程同步,讓調(diào)用線程阻塞等待目標(biāo)線程終止或超時(shí)。
  2. 核心重載:無參(無限等待)、join(long millis)(毫秒超時(shí))、join(long millis, int nanos)(高精度超時(shí),極少使用)。
  3. 關(guān)鍵差異:與sleep()的核心區(qū)別是「釋放對象鎖」和「使用場景」,join()用于線程同步,sleep()用于當(dāng)前線程暫停。
  4. 避坑要點(diǎn):先啟動(dòng)目標(biāo)線程再調(diào)用join()、處理InterruptedException、區(qū)分串行 / 并行等待。
  5. 底層邏輯:基于Object.wait()實(shí)現(xiàn),通過循環(huán)判斷isAlive()避免虛假喚醒,目標(biāo)線程終止時(shí)由 JVM 喚醒等待線程。

到此這篇關(guān)于Java Thread.join()方法使用的文章就介紹到這了,更多相關(guān)Java Thread.join()方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中token的存儲(chǔ)和獲取實(shí)例代碼

    Java中token的存儲(chǔ)和獲取實(shí)例代碼

    關(guān)于java獲取微信Token驗(yàn)證的問題相信很多人都遇見過,尤其是對剛接觸微信開發(fā)的人來說確實(shí)有點(diǎn)棘手,下面這篇文章主要給大家介紹了關(guān)于Java中token存儲(chǔ)和獲取的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • Spring Boot + Vue 基于 RSA 的用戶身份認(rèn)證加密機(jī)制實(shí)現(xiàn)過程

    Spring Boot + Vue 基于 RSA 的用戶身份

    RSA是一種非對稱加密算法,適用于用戶身份認(rèn)證加密,本文介紹了基于RSA的用戶身份認(rèn)證加密機(jī)制的實(shí)現(xiàn),包括前端Vue.js使用jsencrypt庫對用戶名密碼進(jìn)行加密,后端使用RSA私鑰解密驗(yàn)證用戶憑據(jù),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能示例

    Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能示例

    這篇文章主要介紹了Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能,結(jié)合實(shí)例形式分析了Java針對數(shù)字的匹配查找及非數(shù)字替換操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-03-03
  • Spring中的底層架構(gòu)核心概念類型轉(zhuǎn)換器詳解

    Spring中的底層架構(gòu)核心概念類型轉(zhuǎn)換器詳解

    這篇文章主要介紹了Spring中的底層架構(gòu)核心概念類型轉(zhuǎn)換器詳解,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • java9遷移注意問題總結(jié)

    java9遷移注意問題總結(jié)

    本篇文章給大家詳細(xì)整理了java9遷移注意的問題,希望我們整理的內(nèi)容能夠幫助到大家。
    2018-02-02
  • springboot如何讀取自定義配置項(xiàng)

    springboot如何讀取自定義配置項(xiàng)

    這篇文章主要介紹了springboot如何讀取自定義配置項(xiàng)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-05-05
  • Java實(shí)現(xiàn)俄羅斯方塊游戲的示例代碼

    Java實(shí)現(xiàn)俄羅斯方塊游戲的示例代碼

    俄羅斯方塊是一個(gè)最初由阿列克謝帕吉特諾夫在蘇聯(lián)設(shè)計(jì)和編程的益智類視頻游戲。本文將利用Java實(shí)現(xiàn)這一經(jīng)典的小游戲,感興趣的可以動(dòng)手試一試
    2022-03-03
  • java語言圖形用戶登錄界面代碼

    java語言圖形用戶登錄界面代碼

    這篇文章主要為大家詳細(xì)介紹了java語言圖形用戶登錄界面代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • springboot單元測試依賴踩坑記錄

    springboot單元測試依賴踩坑記錄

    這篇文章主要介紹了springboot單元測試依賴踩坑記錄及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 簡單探索 Java 中的惰性計(jì)算

    簡單探索 Java 中的惰性計(jì)算

    這篇文章主要介紹了簡單探索 Java 中的惰性計(jì)算,惰性計(jì)算(盡可能延遲表達(dá)式求值)是許多函數(shù)式編程語言的特性。惰性集合在需要時(shí)提供其元素,無需預(yù)先計(jì)算它們,這帶來了一些好處。,需要的朋友可以參考下
    2019-06-06

最新評論

岱山县| 萍乡市| 尉氏县| 麻城市| 江西省| 定州市| 平顺县| 凤山县| 乌兰浩特市| 英超| 旺苍县| 四子王旗| 札达县| 晋中市| 连平县| 莒南县| 泸定县| 秦皇岛市| 万盛区| 辉南县| 河间市| 拜泉县| 临朐县| 灵石县| 汪清县| 浦城县| 丰城市| 获嘉县| 郸城县| 大同县| 涞水县| 手游| 镇远县| 乐都县| 玉环县| 泗水县| 巴马| 宁远县| 安多县| 凤冈县| 祁阳县|