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

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

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

概要

本文分三個部分對Thread.join()進行分析:

1. join() 的示例和作用

2. join() 源碼分析

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

1. join() 的示例和作用

1.1 示例

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

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

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

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

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

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

1.2 join() 的作用

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

我們來看看在 Java 7 Concurrency Cookbook 中相關(guān)的描述(很清楚地說明了 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.

當我們調(diào)用某個線程的這個方法時,這個方法會掛起調(diào)用線程,直到被調(diào)用線程結(jié)束執(zhí)行,調(diào)用線程才會繼續(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() 一共有三個重載版本,分別是無參、一個參數(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,同步的對象是當前線程實例。

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

(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() 會一直保持鎖。

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

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

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

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

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

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

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

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

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

我理解這個說法的意思,但是這樣描述只會讓讀者更難理解。

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

一點感想:技術(shù)人員寫作技術(shù)文章時,最好盡量避免使用過于口語化的詞匯。

因為這種詞匯歧義比較大,會讓讀者感到更加困惑或形成錯誤的理解。

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

相關(guān)文章

  • Java SpringBoot整合JSP和MyBatis

    Java SpringBoot整合JSP和MyBatis

    這篇文章主要介紹了SpringBoot如何整合JSP和MyBatis以及SpringBoot的基本設(shè)置,感興趣的小伙伴可以參考閱讀
    2023-03-03
  • Java中你真的會用Constructor構(gòu)造器嗎之看完本篇你就真的會了

    Java中你真的會用Constructor構(gòu)造器嗎之看完本篇你就真的會了

    顯式初始化要求我們在寫程序時就確定初始值,這有時很不方便。我們可以使用構(gòu)造器(constructor)來初始化對象。構(gòu)造器可以初始化數(shù)據(jù)成員,還可以規(guī)定特定的操作。這些操作會在創(chuàng)建對象時自動執(zhí)行。下面文字將對該內(nèi)容做詳細介紹,需要的小伙伴請參考
    2021-09-09
  • Java Scanner 類的使用小結(jié)

    Java Scanner 類的使用小結(jié)

    在筆試編程過程中,關(guān)于數(shù)據(jù)的讀取如果迷迷糊糊,那后來的編程即使想法很對,實現(xiàn)很好,也是徒勞,于是在這里認真總結(jié)了Java Scanner 類的使用,需要的朋友可以參考下
    2018-10-10
  • Mybatis事務(wù)如何跟Spring結(jié)合(數(shù)據(jù)庫事務(wù)特性和Spring事務(wù)管理源碼)

    Mybatis事務(wù)如何跟Spring結(jié)合(數(shù)據(jù)庫事務(wù)特性和Spring事務(wù)管理源碼)

    MyBatis與Spring的事務(wù)結(jié)合主要是通過Spring的事務(wù)管理和MyBatis的數(shù)據(jù)庫操作來實現(xiàn)的,在本文中,我們將從數(shù)據(jù)庫事務(wù)特性和Spring事務(wù)管理源碼兩個角度來分析MyBatis事務(wù)如何與Spring結(jié)合到一起的原理,感興趣的朋友一起看看吧
    2024-01-01
  • Java并發(fā)編程中的CyclicBarrier使用解析

    Java并發(fā)編程中的CyclicBarrier使用解析

    這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier使用解析,CyclicBarrier從字面意思上來看,循環(huán)柵欄,這篇文章就來分析下是到底是如何實現(xiàn)循環(huán)和柵欄的,需要的朋友可以參考下
    2023-12-12
  • Mybatis使用@Select注解sql中使用in問題

    Mybatis使用@Select注解sql中使用in問題

    這篇文章主要介紹了Mybatis使用@Select注解sql中使用in問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • SpringBoot集成quartz實現(xiàn)定時任務(wù)

    SpringBoot集成quartz實現(xiàn)定時任務(wù)

    這篇文章主要介紹了如何使用SpringBoot整合Quartz,并將定時任務(wù)寫入庫中(持久化存儲),還可以任意對定時任務(wù)進行如刪除、暫停、恢復(fù)等操作,需要的可以了解下
    2023-09-09
  • Springboot詳解整合SpringSecurity實現(xiàn)全過程

    Springboot詳解整合SpringSecurity實現(xiàn)全過程

    Spring Security基于Spring開發(fā),項目中如果使用Springboot作為基礎(chǔ),配合Spring Security做權(quán)限更加方便,而Shiro需要和Spring進行整合開發(fā)。因此作為spring全家桶中的Spring Security在java領(lǐng)域很常用
    2022-07-07
  • Java的微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例

    Java的微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例

    這篇文章主要介紹了Java微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例,注意一下json-lib所需要的jar包,需要的朋友可以參考下
    2016-02-02
  • Maven項目在new module后,pom文件顯示為Ignored pom.xml問題

    Maven項目在new module后,pom文件顯示為Ignored pom.xml問題

    在Maven項目中,若創(chuàng)建過同名module后刪除,再次創(chuàng)建時可能導(dǎo)致pom.xml文件被IDEA忽略,原因是IDEA保留了之前module的痕跡,導(dǎo)致重建時將其視為已刪除的module,解決方法是進入IDEA設(shè)置,找到Maven的Ignored Files設(shè)置
    2024-09-09

最新評論