欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?Thread中join方法使用舉例詳解

 更新時間:2025年07月31日 11:23:04   作者:干凈的壞蛋  
在Java編程語言中,Thread.join()方法是一個非常重要的同步工具,它允許一個線程(調(diào)用者)等待另一個線程(被調(diào)用者)執(zhí)行完成,這篇文章主要介紹了Java?Thread中join方法使用的相關(guān)資料,需要的朋友可以參考下

前言

join() 方法是 Java 并發(fā)編程中一個非常重要且基礎(chǔ)的方法,它允許一個線程等待另一個線程執(zhí)行完成。理解它的工作原理和使用場景對于編寫健壯的多線程應(yīng)用程序至關(guān)重要。

1.join()方法的定義和作用

join()java.lang.Thread 類的一個實例方法。它的核心作用是阻塞當(dāng)前正在執(zhí)行的線程(我們稱之為“主線程”,盡管它可以是任何線程),直到調(diào)用 join() 方法的那個線程(我們稱之為“目標(biāo)線程”)執(zhí)行完畢。

簡單來說,如果線程 A 的代碼中調(diào)用了線程 B 的 join() 方法(即 B.join()),那么線程 A 就會進(jìn)入等待狀態(tài),直到線程 B 執(zhí)行結(jié)束,線程 A 才會從 B.join() 這行代碼繼續(xù)往下執(zhí)行。

這就像在一個接力賽中,下一棒的選手(線程 A)必須等待上一棒的選手(線程 B)跑完自己的賽程并交接后,才能開始跑。

2.join()方法的三個重載版本

Thread 類提供了三個版本的 join() 方法:

  1. public final void join()

    • 這是最常用的版本。它會一直等待,直到目標(biāo)線程執(zhí)行完畢。
    • 它會拋出 InterruptedException,這意味著等待中的線程可能會被中斷。
  2. public final synchronized void join(long millis)

    • 這是一個帶超時參數(shù)的版本。它會等待目標(biāo)線程執(zhí)行,但最多只等待指定的毫秒數(shù)。
    • 如果目標(biāo)線程在指定的超時時間內(nèi)執(zhí)行完畢,當(dāng)前線程會立即繼續(xù)執(zhí)行。
    • 如果超過了指定的毫秒數(shù),目標(biāo)線程還未執(zhí)行完,當(dāng)前線程也會停止等待,繼續(xù)執(zhí)行。
    • 同樣會拋出 InterruptedException。
  3. public final synchronized void join(long millis, int nanos)

    • 這個版本提供了更精確的超時控制,可以指定毫秒和納秒。
    • 功能與 join(long millis) 類似,只是提供了更高精度的時間控制。

3.join()方法的工作原理 (深入分析)

你可能會好奇,join() 是如何實現(xiàn)線程等待的?它的底層原理是什么?

  1. isAlive() 檢查: 當(dāng)你在線程 A 中調(diào)用 threadB.join() 時,join() 方法內(nèi)部會首先檢查 threadB 是否還活著 (isAlive())。如果 threadB 已經(jīng)執(zhí)行完畢(或者從未啟動),isAlive() 會返回 false,那么 join() 方法會立即返回,線程 A 不會進(jìn)行任何等待。

  2. wait() 循環(huán)等待: 如果 isAlive() 返回 truejoin() 方法會進(jìn)入一個循環(huán)。在這個循環(huán)里,它會調(diào)用目標(biāo)線程對象(threadB)的 wait() 方法。

    • 關(guān)鍵點: join() 方法是一個 synchronized 方法。當(dāng)線程 A 調(diào)用 threadB.join() 時,它會獲取 threadB 這個對象的鎖。然后,join() 方法內(nèi)部調(diào)用 threadB.wait(),這會導(dǎo)致線程 A 釋放 threadB 的鎖,并進(jìn)入 threadB 對象的等待集(Wait Set)中,狀態(tài)變?yōu)?WAITINGTIMED_WAITING。
  3. JVM 負(fù)責(zé)喚醒: 當(dāng)目標(biāo)線程 threadBrun() 方法執(zhí)行完畢,準(zhǔn)備退出時,Java 虛擬機(JVM)會在其內(nèi)部自動調(diào)用 threadBnotifyAll() 方法。

    • 這個 notifyAll() 會喚醒所有正在等待 threadB 對象鎖的線程,當(dāng)然也包括了之前因調(diào)用 join() 而等待的線程 A。
  4. 重新獲取鎖并退出: 線程 A 被喚醒后,會嘗試重新獲取 threadB 的對象鎖。一旦獲取成功,它會從 wait() 方法返回,并退出 join() 的循環(huán),從而結(jié)束等待,繼續(xù)執(zhí)行后續(xù)代碼。

總結(jié)一下核心機制: join() 的實現(xiàn)巧妙地利用了 Java 的內(nèi)置鎖(synchronized)和 wait()/notifyAll() 協(xié)作機制。它將線程間的執(zhí)行順序問題,轉(zhuǎn)化為了經(jīng)典的“生產(chǎn)者-消費者”模式中的線程同步問題。

4. 為什么要使用join()?使用場景

join() 的主要應(yīng)用場景是協(xié)調(diào)線程間的執(zhí)行順序。當(dāng)你需要確保一個或多個前置任務(wù)(在子線程中執(zhí)行)完成后,主線程才能繼續(xù)執(zhí)行后續(xù)任務(wù)時,join() 就非常有用。

常見場景:

  • 數(shù)據(jù)初始化: 主線程啟動多個子線程去分別加載不同的資源或執(zhí)行初始化計算。主線程需要等待所有初始化工作完成后,才能使用這些資源去執(zhí)行核心業(yè)務(wù)邏輯。
  • 分塊處理: 一個大任務(wù)被拆分成多個小任務(wù),每個小任務(wù)交給一個子線程處理。主線程需要等待所有子線程處理完畢后,對結(jié)果進(jìn)行匯總和合并。
  • 確保流程順序: 在一個復(fù)雜的業(yè)務(wù)流程中,步驟 B 必須在步驟 A 執(zhí)行完畢后才能開始。如果 A 和 B 在不同的線程中,那么 B 所在的線程就需要 join() A 所在的線程。

5. 代碼示例

示例 1: 等待單個線程

public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主線程開始運行...");

        Thread workerThread = new Thread(() -> {
            System.out.println("子線程開始工作...");
            try {
                // 模擬耗時操作
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子線程工作完成。");
        });

        workerThread.start(); // 啟動子線程

        System.out.println("主線程調(diào)用 join() 等待子線程...");
        workerThread.join(); // 關(guān)鍵:主線程在此處阻塞,等待 workerThread 執(zhí)行完畢

        System.out.println("主線程在子線程完成后繼續(xù)運行。");
    }
}

輸出:

主線程開始運行...
主線程調(diào)用 join() 等待子線程...
子線程開始工作...
// (等待約 2 秒)
子線程工作完成。
主線程在子線程完成后繼續(xù)運行。

示例 2: 等待多個線程

public class MultiJoinExample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主線程開始...");

        Thread t1 = new Thread(() -> {
            System.out.println("線程1 開始處理...");
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
            System.out.println("線程1 處理完畢。");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("線程2 開始處理...");
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            System.out.println("線程2 處理完畢。");
        });

        // 啟動所有線程
        t1.start();
        t2.start();

        System.out.println("主線程等待所有子線程完成...");

        // 等待 t1 和 t2
        t1.join();
        t2.join();

        System.out.println("所有子線程處理完畢,主線程繼續(xù)執(zhí)行。");
    }
}

輸出:

主線程開始...
主線程等待所有子線程完成...
線程1 開始處理...
線程2 開始處理...
// (等待約 1 秒)
線程1 處理完畢。
// (再等待約 1 秒)
線程2 處理完畢。
所有子線程處理完畢,主線程繼續(xù)執(zhí)行。

6.InterruptedException的處理

join() 方法會拋出 InterruptedException。這是因為正在等待的線程(調(diào)用 join() 的線程)自身可能會被其他線程中斷 (interrupt())。

當(dāng)一個線程在 join() 等待期間被中斷時,join() 方法會立即拋出 InterruptedException。在 catch 塊中,通常需要處理這個中斷信號。一個好的實踐是重新設(shè)置當(dāng)前線程的中斷狀態(tài),以便上層調(diào)用者能夠知道發(fā)生了中斷。

try {
    workerThread.join();
} catch (InterruptedException e) {
    // 捕獲到中斷異常
    System.out.println("主線程在等待期間被中斷了!");
    // 恢復(fù)中斷狀態(tài),這是一種良好的編程實踐
    Thread.currentThread().interrupt();
}

總結(jié)

特性描述
目的阻塞當(dāng)前線程,等待目標(biāo)線程執(zhí)行完畢。
核心原理基于 synchronizedwait() / notifyAll() 機制。
版本join(), join(long millis), join(long millis, int nanos)。
使用場景需要保證線程執(zhí)行順序,如等待子任務(wù)完成、數(shù)據(jù)初始化等。
異常拋出 InterruptedException,需要妥善處理線程中斷。
關(guān)鍵點t.join() 是讓當(dāng)前線程等待 t 線程,而不是讓 t 線程等待。

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

相關(guān)文章

  • 深入理解spring多數(shù)據(jù)源配置

    深入理解spring多數(shù)據(jù)源配置

    項目中我們經(jīng)常會遇到多數(shù)據(jù)源的問題,尤其是數(shù)據(jù)同步或定時任務(wù)等項目更是如此。本篇文章主要介紹了spring多數(shù)據(jù)源配置,有興趣的可以了解一下。
    2017-01-01
  • Java中HashMap集合的常用方法詳解

    Java中HashMap集合的常用方法詳解

    本篇文章給大家?guī)淼膬?nèi)容是關(guān)于Java中HashMap集合的常用方法詳解,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。下面我們就來學(xué)習(xí)一下吧
    2021-11-11
  • Java實現(xiàn)冒泡排序

    Java實現(xiàn)冒泡排序

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)冒泡排序,把一列數(shù)組按從小到大或從大到小排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • spring框架cacheAnnotation緩存注釋聲明解析

    spring框架cacheAnnotation緩存注釋聲明解析

    這篇文章主要介紹了spring框架中cacheAnnotation注釋聲明緩存解析示例有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-10-10
  • spring的構(gòu)造函數(shù)注入屬性@ConstructorBinding用法

    spring的構(gòu)造函數(shù)注入屬性@ConstructorBinding用法

    這篇文章主要介紹了關(guān)于spring的構(gòu)造函數(shù)注入屬性@ConstructorBinding用法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java 程序員掌握 Spring Boot非常有必要

    Java 程序員掌握 Spring Boot非常有必要

    本文帶領(lǐng)大家一起來了解下 Spring Boot 到底是什么?包括springboot的誕生,springboot特征具有哪些優(yōu)勢,如何讓開發(fā)變得更簡單,帶著這些問題一起通過本文學(xué)習(xí)下吧
    2021-06-06
  • Java中死鎖與活鎖的具體實現(xiàn)

    Java中死鎖與活鎖的具體實現(xiàn)

    鎖發(fā)生在不同的請求中,本文主要介紹了Java中死鎖與活鎖,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java多線程的調(diào)度_動力節(jié)點Java學(xué)院整理

    Java多線程的調(diào)度_動力節(jié)點Java學(xué)院整理

    有多個線程,如何控制它們執(zhí)行的先后次序呢?下文給大家分享四種方法及java多線程調(diào)度的實例代碼,需要的朋友參考下吧
    2017-05-05
  • 反編譯jar實現(xiàn)的三種方式

    反編譯jar實現(xiàn)的三種方式

    本文主要介紹了反編譯jar實現(xiàn)的三種方式,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • JAVA實現(xiàn)深拷貝的幾種方式代碼

    JAVA實現(xiàn)深拷貝的幾種方式代碼

    這篇文章主要給大家介紹了關(guān)于JAVA實現(xiàn)深拷貝的幾種方式,在Java中深拷貝和淺拷貝是用來復(fù)制對象的兩種不同方式,深拷貝會對所有數(shù)據(jù)類型進(jìn)行拷貝,包括對象所包含的內(nèi)部對象,需要的朋友可以參考下
    2023-09-09

最新評論