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

Java?Thread?join()的使用場景和原理詳解

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

1、使用場景

一般情況下,主線程創(chuàng)建并啟動子線程,如果子線程中執(zhí)行大量耗時運算,主線程可能早于子線程結(jié)束。如果主線程需要知道子線程的執(zhí)行結(jié)果時,就需要等待子線程執(zhí)行結(jié)束。主線程可以sleep(xx),但這樣的xx時間不好確定,因為子線程的執(zhí)行時間不確定,join()方法比較合適這個場景。

2、join()方法定義和使用

join()是Thread類的一個方法。根據(jù)jdk文檔的定義:

public final void join()throws InterruptedException: Waits for this thread to die.

join()方法的作用,是等待這個線程結(jié)束;這個定義比較模糊,個人認為"Java 7 Concurrency Cookbook"的定義較為清晰:

Waiting for the finalization of a thread

In some situations, we will have to wait for the finalization of a thread. For example, we may have a program that will begin initializing the resources it needs before proceeding with the rest of the execution. 
We can run the initialization tasks as threads and wait for its finalization before continuing with the rest of the program. 
For this purpose, we can use the join() method of the Thread class. When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.

解釋一下,是主線程等待子線程的終止。也就是說主線程的代碼塊中,如果碰到了t.join()方法,此時主線程需要等待(阻塞),等待子線程結(jié)束了(Waits for this thread to die.),才能繼續(xù)執(zhí)行t.join()之后的代碼塊。

來看一個join()的案例demo:

public class JoinDemo {

    public static void main(String[] args) throws InterruptedException {
        //獲取當(dāng)前線程信息
       Thread previousThread= Thread.currentThread();
        for(int i=0;i<10;i++){
            Thread thread=new Thread(new Domino(previousThread));
            thread.start();
            previousThread=thread;
        }
        TimeUnit.SECONDS.sleep(5);
        System.out.println("Thread.currentThread().getName()+\" terminate.\" = " + Thread.currentThread().getName()+" terminate.");

    }

    static class Domino implements Runnable{
        private Thread thread;
        public Domino(Thread thread){
            this.thread=thread;
        }
        @Override
        public void run() {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " terminate.");
        }
    }
}

這段demo的邏輯就是當(dāng)前線程等待它前面的線程執(zhí)行結(jié)束再執(zhí)行,輸出如下:

Thread.currentThread().getName()+" terminate." = main terminate.
Thread-0 terminate
Thread-1 terminate
Thread-2 terminate
Thread-3 terminate
Thread-4 terminate
Thread-5 terminate
Thread-6 terminate
Thread-7 terminate
Thread-8 terminate
Thread-9 terminate

3、源碼分析

public final void join() throws InterruptedException {
    join(0);
}

再看join(long millis)方法:

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");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

isAlive() 是 Thread 內(nèi)部的一個 native 方法,只要當(dāng)線程處于 NEW 和 TERMINATED 狀態(tài)時返回false,其余都返回true。join() 其實就是通過調(diào)用 wait() 方法(Object定義的,詳見線程間通信),wait(0) 表示當(dāng)前線程立即釋放鎖(這里的鎖指的就是前一個線程)并進入 waiting 狀態(tài),等待其他線程喚醒(notify/notifyAll)。

join() 一共有三個重載版本,分別是無參、一個參數(shù)、兩個參數(shù):

public final void join() throws InterruptedException;

 public final synchronized void join(long millis) throws InterruptedException;
 
 public final synchronized void join(long millis, int nanos) throws InterruptedException;

(1) 三個方法都被final修飾,無法被子類重寫。

(2) join(long), join(long, long) 是synchronized method,同步的對象是當(dāng)前線程實例。

(3) join() 和 join(0) 是等價的,表示一直等下去;join(非0)表示等待一段時間。

從源碼可以看到 join(0) 調(diào)用了Object.wait(0),其中Object.wait(0) 會一直等待,直到被notify/中斷才返回。

while(isAlive())是為了防止子線程偽喚醒(spurious wakeup),只要子線程沒有TERMINATED的,父線程就需要繼續(xù)等下去。

(4) join() 和 sleep() 一樣,可以被中斷(被中斷時,會拋出 InterrupptedException 異常);不同的是,join() 內(nèi)部調(diào)用了 wait(),會出讓鎖,而 sleep() 會一直保持鎖。

4、join() 方法注意點

4.1、join() 與 start() 的調(diào)用順序

package com.dxz.join;
class BThread extends Thread {
    public BThread() {
        super("[BThread] Thread");
    };
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + " loop at " + i);
                Thread.sleep(1000);
            }
            System.out.println(threadName + " end.");
        } catch (Exception e) {
            System.out.println("Exception from " + threadName + ".run");
        }
    }
}

public class TestDemo {
    public static void main(String[] args) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        BThread bt = new BThread();
        try {
            bt.join();
            bt.start();
            Thread.sleep(2000);
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

執(zhí)行結(jié)果:

 main start.
[BThread] Thread start.
[BThread] Thread loop at 0
[BThread] Thread loop at 1
main end!
[BThread] Thread loop at 2
[BThread] Thread loop at 3
[BThread] Thread loop at 4
[BThread] Thread end.

main線程沒有等待 [BThread] 線程執(zhí)行完再執(zhí)行。join方法必須在線程start方法調(diào)用之后調(diào)用才有意義。這個也很容易理解:線程沒有執(zhí)行,isAlive() 方法就不會返回true,也就不會執(zhí)行 wait(0) 方法了。

4.2、join() 與中斷異常

在join()過程中,如果當(dāng)前線程被中斷,則當(dāng)前線程出現(xiàn)異常。(注意是調(diào)用thread.join()的線程被中斷才會進入異常,比如a線程調(diào)用b.join(),a中斷會報異常而b中斷不會異常)

下面總結(jié)下中斷與 wait()/join()/sleep() 的使用:

調(diào)用interrupt()方法,立刻改變的是中斷狀態(tài),但如果不是在阻塞態(tài),就不會拋出異常;如果在進入阻塞態(tài)后,中斷狀態(tài)為已中斷,就會立刻拋出異常,但同時中斷標(biāo)志也會被系統(tǒng)復(fù)位。

(1)sleep() &interrupt()

線程A正在使用sleep()暫停著: Thread.sleep(100000),如果要取消它的等待狀態(tài),可以在正在執(zhí)行的線程里(比如這里是B)調(diào)用a.interrupt()[a是線程A對應(yīng)到的Thread實例],令線程A放棄睡眠操作。即,在線程B中執(zhí)行a.interrupt(),處于阻塞中的線程a將放棄睡眠操作。

當(dāng)在sleep中時線程被調(diào)用interrupt()時,就馬上會放棄暫停的狀態(tài)并拋出InterruptedException。拋出異常的,是A線程。

(2)wait() &interrupt()

線程A調(diào)用了wait()進入了等待狀態(tài),也可以用interrupt()取消。不過這時候要注意鎖定的問題。線程在進入等待區(qū),會把鎖定解除,當(dāng)對等待中的線程調(diào)用interrupt()時,會先重新獲取鎖定,再拋出異常。在獲取鎖定之前,是無法拋出異常的。

(3)join() &interrupt()

當(dāng)線程以join()等待其他線程結(jié)束時,當(dāng)它被調(diào)用interrupt(),它與sleep()時一樣,會馬上跳到catch塊里.。

注意,調(diào)用的interrupt()方法,一定是調(diào)用被阻塞線程的interrupt方法。如在線程a中調(diào)用線程t.interrupt()。

總結(jié)

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

相關(guān)文章

  • Nacos快速安裝部署教程

    Nacos快速安裝部署教程

    文章簡要介紹Nacos作為阿里巴巴開源的微服務(wù)管理組件,涵蓋其核心功能及單機模式部署步驟:下載穩(wěn)定版、配置MySQL、執(zhí)行數(shù)據(jù)庫腳本、啟動服務(wù)并查看日志,最后通過指定地址和賬號登錄控制臺
    2025-07-07
  • springboot過濾器執(zhí)行兩次的解決及跨域過濾器問題

    springboot過濾器執(zhí)行兩次的解決及跨域過濾器問題

    這篇文章主要介紹了springboot過濾器執(zhí)行兩次的解決及跨域過濾器問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 詳解JAVA 字節(jié)流和字符流

    詳解JAVA 字節(jié)流和字符流

    這篇文章主要介紹了JAVA 字節(jié)流和字符流的的相關(guān)資料,文中講解非常的細致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Spring Security實現(xiàn)5次密碼錯誤觸發(fā)賬號自動鎖定功能

    Spring Security實現(xiàn)5次密碼錯誤觸發(fā)賬號自動鎖定功能

    在現(xiàn)代互聯(lián)網(wǎng)應(yīng)用中,賬號安全是重中之重,然而,暴力 破解攻擊依然是最常見的安全威脅之一,攻擊者通過自動化腳本嘗試大量的用戶名和密碼組合,試圖找到漏洞進入系統(tǒng),所以為了解決這一問題,賬號鎖定機制被廣泛應(yīng)用,本文介紹了Spring Security實現(xiàn)5次密碼錯誤觸發(fā)賬號鎖定功能
    2024-12-12
  • SpringBoot+logback默認日志的配置和使用方式

    SpringBoot+logback默認日志的配置和使用方式

    這篇文章主要介紹了SpringBoot+logback默認日志的配置和使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java后端向前端返回文件流實現(xiàn)下載功能的方法

    Java后端向前端返回文件流實現(xiàn)下載功能的方法

    這篇文章主要給大家介紹了關(guān)于Java后端向前端返回文件流實現(xiàn)下載功能的相關(guān)資料,Java后端可以通過調(diào)用接口返回文件流來實現(xiàn)文件傳輸功能,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • Java實戰(zhàn)之實現(xiàn)用戶登錄

    Java實戰(zhàn)之實現(xiàn)用戶登錄

    這篇文章主要介紹了Java實戰(zhàn)之實現(xiàn)用戶登錄,文中有非常詳細的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • mybatisplus之Wrappers.ne踩坑記錄解決

    mybatisplus之Wrappers.ne踩坑記錄解決

    這篇文章主要為大家介紹了mybatisplus之Wrappers.ne踩坑記錄解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • Spring Boot 實例代碼之通過接口安全退出

    Spring Boot 實例代碼之通過接口安全退出

    這篇文章主要介紹了Spring Boot 實例代碼之通過接口安全退出的相關(guān)資料,需要的朋友可以參考下
    2017-09-09
  • Java虛擬機GC的各種缺點匯總

    Java虛擬機GC的各種缺點匯總

    Java通過垃圾收集器(Garbage Collection,簡稱GC)實現(xiàn)自動內(nèi)存管理,這樣可有效減輕Java應(yīng)用開發(fā)人員的負擔(dān),也避免了更多內(nèi)存泄露的風(fēng)險,這篇文章主要介紹了Java虛擬機GC的種種缺點,感興趣的朋友一起看看吧
    2025-05-05

最新評論

静海县| 郑州市| 金溪县| 昭觉县| 建水县| 盐源县| 庄浪县| 新建县| 大荔县| 延寿县| 冀州市| 垫江县| 盐源县| 祁门县| 盐源县| 锡林浩特市| 厦门市| 江油市| 乐陵市| 黑河市| 曲水县| 吉木萨尔县| 渑池县| 雷州市| 鱼台县| 赤峰市| 慈溪市| 罗定市| 尼勒克县| 凯里市| 民丰县| 建始县| 海兴县| 安阳市| 澳门| 隆林| 汉源县| 基隆市| 华池县| 虎林市| 兰坪|