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

Java中Thread.join()的使用方法

 更新時(shí)間:2020年07月31日 14:53:27   作者:huangzejun  
這篇文章主要介紹了Java中Thread.join()的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

概要

本文分三個(gè)部分對(duì)Thread.join()進(jìn)行分析:

1. join() 的示例和作用

2. join() 源碼分析

3. 對(duì)網(wǎng)上其他分析 join() 的文章提出疑問(wèn)

1. join() 的示例和作用

1.1 示例

// 父線程
public class Parent {
 public static void main(String[] args) {
  // 創(chuàng)建child對(duì)象,此時(shí)child表示的線程處于NEW狀態(tài)
  Child child = new Child();
  // child表示的線程轉(zhuǎn)換為RUNNABLE狀態(tài)
  child.start();
  // 等待child線程運(yùn)行完再繼續(xù)運(yùn)行
  child.join();
 }
}
// 子線程
public class Child extends Thread {
 public void run() {
  // ...
 }
}

上面代碼展示了兩個(gè)類(lèi):Parent(父線程類(lèi)),Child(子線程類(lèi))。

Parent.main()方法是程序的入口,通過(guò)Child child = new Child(); 新建child子線程(此時(shí) child子線程處于NEW狀態(tài));

然后調(diào)用child.start()(child子線程狀態(tài)轉(zhuǎn)換為RUNNABLE);

再調(diào)用child.join(),此時(shí),Parent父線程會(huì)等待child子線程運(yùn)行完再繼續(xù)運(yùn)行。

下圖是我總結(jié)的 Java 線程狀態(tài)轉(zhuǎn)換圖:

1.2 join() 的作用

讓父線程等待子線程結(jié)束之后才能繼續(xù)運(yùn)行。

我們來(lái)看看在 Java 7 Concurrency Cookbook 中相關(guān)的描述(很清楚地說(shuō)明了 join() 的作用):

Waiting for the finalization of a thread

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

當(dāng)我們調(diào)用某個(gè)線程的這個(gè)方法時(shí),這個(gè)方法會(huì)掛起調(diào)用線程,直到被調(diào)用線程結(jié)束執(zhí)行,調(diào)用線程才會(huì)繼續(xù)執(zhí)行。

2. join() 源碼分析

以下是 JDK 8 中 join() 的源碼:

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

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

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

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

 if (nanos < 0 || nanos > 999999) {
  throw new IllegalArgumentException(
       "nanosecond timeout value out of range");
 }

 if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
  millis++;
 }

 join(millis);
}

join() 一共有三個(gè)重載版本,分別是無(wú)參、一個(gè)參數(shù)、兩個(gè)參數(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)三個(gè)方法都被final修飾,無(wú)法被子類(lèi)重寫(xiě)。

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

(2)無(wú)參版本和兩個(gè)參數(shù)版本最終都調(diào)用了一個(gè)參數(shù)的版本。

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

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

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

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

以本文開(kāi)頭的代碼為例,我們分析一下代碼邏輯:

調(diào)用鏈:Parent.main() -> child.join() -> child.join(0) -> child.wait(0)(此時(shí) Parent線程會(huì)獲得 child 實(shí)例作為鎖,其他線程可以進(jìn)入 child.join() ,但不可以進(jìn)入 child.join(0), 因?yàn)閏hild.join(0)是同步方法)。

如果 child 線程是 Active,則調(diào)用 child.wait(0)(為了防止子線程 spurious wakeup, 需要將 wait(0) 放入while(isAlive())循環(huán)中。

一旦 child 線程不為 Active (狀態(tài)為 TERMINATED),child.notifyAll()會(huì)被調(diào)用-> child.wait(0)返回 -> child.join(0)返回 -> child.join()返回 -> Parent.main()繼續(xù)執(zhí)行, 子線程會(huì)調(diào)用this.notify(),child.wait(0)會(huì)返回到child.join(0) ,child.join(0)會(huì)返回到 child.join(), child.join() 會(huì)返回到 Parent 父線程,Parent 父線程就可以繼續(xù)運(yùn)行下去了。

3. 對(duì)網(wǎng)上其他分析 join() 的文章提出疑問(wèn)

我覺(jué)得網(wǎng)上很多文章的描述有歧義,下面挑選一些描述進(jìn)行分析,也歡迎大家留言一起討論。

a. 子線程結(jié)束之后,"會(huì)喚醒主線程",父線程重新獲取cpu執(zhí)行權(quán),繼續(xù)運(yùn)行。

這里感謝kerwinX的留言,子線程結(jié)束后,子線程的this.notifyAll()會(huì)被調(diào)用,join()返回,父線程只要獲取到鎖和CPU,就可以繼續(xù)運(yùn)行下去了。

b. join() 將幾個(gè)并行的線程"合并為一個(gè)單線程"執(zhí)行。

我理解這個(gè)說(shuō)法的意思,但是這樣描述只會(huì)讓讀者更難理解。

在調(diào)用 join() 方法的程序中,原來(lái)的多個(gè)線程仍然多個(gè)線程,并沒(méi)有發(fā)生“合并為一個(gè)單線程”。真正發(fā)生的是調(diào)用join() 的線程進(jìn)入 TIMED_WAITING 狀態(tài),等待 join() 所屬線程運(yùn)行結(jié)束后再繼續(xù)運(yùn)行。

一點(diǎn)感想:技術(shù)人員寫(xiě)作技術(shù)文章時(shí),最好盡量避免使用過(guò)于口語(yǔ)化的詞匯。

因?yàn)檫@種詞匯歧義比較大,會(huì)讓讀者感到更加困惑或形成錯(cuò)誤的理解。

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

相關(guān)文章

  • Java并發(fā)系列之ConcurrentHashMap源碼分析

    Java并發(fā)系列之ConcurrentHashMap源碼分析

    這篇文章主要為大家詳細(xì)分析了Java并發(fā)系列之ConcurrentHashMap源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • Java軟件生產(chǎn)監(jiān)控工具Btrace使用方法詳解

    Java軟件生產(chǎn)監(jiān)控工具Btrace使用方法詳解

    這篇文章主要介紹了Java軟件生產(chǎn)監(jiān)控工具Btrace使用方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例)

    Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例)

    這篇文章主要介紹了Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例),需要的朋友可以參考下
    2017-09-09
  • Java使用easyExcel批量導(dǎo)入數(shù)據(jù)詳解

    Java使用easyExcel批量導(dǎo)入數(shù)據(jù)詳解

    這篇文章主要介紹了Java使用easyExcel批量導(dǎo)入數(shù)據(jù)詳解,通常我們會(huì)提供一個(gè)模板,此模塊我們可以使用easyExcel導(dǎo)出數(shù)據(jù)生成的一個(gè)Excel文件當(dāng)作模板,提供下載鏈接,用戶(hù)在該文件內(nèi)填入規(guī)定的數(shù)據(jù)格式以后可以批量導(dǎo)入數(shù)據(jù)到數(shù)據(jù)庫(kù)中,需要的朋友可以參考下
    2023-08-08
  • Java8 lambda表達(dá)式2種常用方法代碼解析

    Java8 lambda表達(dá)式2種常用方法代碼解析

    這篇文章主要介紹了Java8 lambda表達(dá)式2種常用方法代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 深度解析Java DTO(最新推薦)

    深度解析Java DTO(最新推薦)

    DTO(Data Transfer Object)是一種用于在不同層(如Controller層、Service層)之間傳輸數(shù)據(jù)的對(duì)象設(shè)計(jì)模式,其核心目的是封裝數(shù)據(jù),減少網(wǎng)絡(luò)傳輸開(kāi)銷(xiāo),同時(shí)避免直接暴露領(lǐng)域模型(Entity)的內(nèi)部結(jié)構(gòu),感興趣的朋友一起看看吧
    2025-06-06
  • JAVA關(guān)鍵字及作用詳解

    JAVA關(guān)鍵字及作用詳解

    本文主要介紹了Java關(guān)鍵字及作用,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • RestTemplate中文亂碼配置方式

    RestTemplate中文亂碼配置方式

    這篇文章討論了在使用RestTemplate時(shí),由于編碼問(wèn)題導(dǎo)致中文顯示為問(wèn)號(hào)的問(wèn)題,分析了原因,指出RestTemplate默認(rèn)使用ISO-8859-1編碼,并建議將messageConverters的編碼改為UTF-8來(lái)解決此問(wèn)題,提供了具體的配置方法,如修改applicationContext.xml中的相關(guān)設(shè)置
    2025-10-10
  • mybatis-plus的批量新增/批量更新以及問(wèn)題

    mybatis-plus的批量新增/批量更新以及問(wèn)題

    這篇文章主要介紹了Mybatis-Plus實(shí)現(xiàn)批量新增與批量更新以及出現(xiàn)的問(wèn)題,文章中有詳細(xì)的代碼示例,感興趣的同學(xué)可以參考一下
    2023-04-04
  • 一次踩坑記錄 @valid注解不生效 排查過(guò)程

    一次踩坑記錄 @valid注解不生效 排查過(guò)程

    這篇文章主要介紹了一次踩坑記錄 @valid注解不生效 排查過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06

最新評(píng)論

绥化市| 安多县| 民勤县| 贞丰县| 来安县| 湖口县| 嘉定区| 东光县| 都兰县| 自贡市| 平和县| 社会| 宜兰市| 甘孜县| 东明县| 汤阴县| 辽宁省| 凤冈县| 马鞍山市| 利辛县| 香港| 洞口县| 香河县| 织金县| 开原市| 荔浦县| 出国| 辉县市| 板桥市| 东山县| 犍为县| 武乡县| 神农架林区| 额济纳旗| 福泉市| 盐山县| 海安县| 中山市| 龙里县| 密云县| 屯留县|