Java中的Thread.join()详解

 更新时间:2023年09月14日 10:36:13   作者:Chen洋  
这篇文章主要介绍了Thread.join()详解 ,join是Thread类的一个方法,启动线程后直接调用,本文通过实例代码介绍了join方法的作用及用法详解,需要的朋友可以参考下

一、使用方式。

join是Thread类的一个方法,启动线程后直接调用,例如:

Thread t = new AThread(); t.start(); t.join();

二、为什么要用join()方法

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

三、join方法的作用

在JDk的API里对于join()方法是:

即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。

四、用实例来理解

写一个简单的例子来看一下join()的用法:

1.AThread 类

  • BThread类

  • TestDemo 类

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");
        }
    }
}
class AThread extends Thread {
    BThread bt;
    public AThread(BThread bt) {
        super("[AThread] Thread");
        this.bt = bt;
    }
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        try {
            bt.join();
            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();
        AThread at = new AThread(bt);
        try {
            bt.start();
            Thread.sleep(2000);
            at.start();
            at.join();
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

打印结果:

main start.    //主线程起动,因为调用了at.join(),要等到at结束了,此线程才能向下执行。 
[BThread] Thread start. 
[BThread] Thread loop at 0 
[BThread] Thread loop at 1 
[AThread] Thread start.    //线程at启动,因为调用bt.join(),等到bt结束了才向下执行。 
[BThread] Thread loop at 2 
[BThread] Thread loop at 3 
[BThread] Thread loop at 4 
[BThread] Thread end. 
[AThread] Thread end.    // 线程AThread在bt.join();阻塞处起动,向下继续执行的结果 
main end!      //线程AThread结束,此线程在at.join();阻塞处起动,向下继续执行的结果。

修改一下代码:

public class TestDemo {
    public static void main(String[] args) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        BThread bt = new BThread();
        AThread at = new AThread(bt);
        try {
            bt.start();
            Thread.sleep(2000);
            at.start();
            //at.join(); //在此处注释掉对join()的调用
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

打印结果:

main start.    // 主线程起动,因为Thread.sleep(2000),主线程没有马上结束;

[BThread] Thread start.    //线程BThread起动
[BThread] Thread loop at 0
[BThread] Thread loop at 1
main end!   // 在sleep两秒后主线程结束,AThread执行的bt.join();并不会影响到主线程。
[AThread] Thread start.    //线程at起动,因为调用了bt.join(),等到bt结束了,此线程才向下执行。
[BThread] Thread loop at 2
[BThread] Thread loop at 3
[BThread] Thread loop at 4
[BThread] Thread end.    //线程BThread结束了
[AThread] Thread end.    // 线程AThread在bt.join();阻塞处起动,向下继续执行的结果

五、从源码看join()方法

在AThread的run方法里,执行了bt.join();,进入看一下它的JDK源码:

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

然后进入join(0L)方法:

public final synchronized void join(long l)
    throws InterruptedException
{
    long l1 = System.currentTimeMillis();
    long l2 = 0L;
    if(l < 0L)
        throw new IllegalArgumentException("timeout value is negative");
    if(l == 0L)
        for(; isAlive(); wait(0L));
    else
        do
        {
            if(!isAlive())
                break;
            long l3 = l - l2;
            if(l3 <= 0L)
                break;
            wait(l3);
            l2 = System.currentTimeMillis() - l1;
        } while(true);
}

单纯从代码上看: * 如果线程被生成了,但还未被起动,isAlive()将返回false,调用它的join()方法是没有作用的。将直接继续向下执行。

         * 在AThread类中的run方法中,bt.join()是判断bt的active状态,如果bt的isActive()方法返回false,在bt.join(),这一点就不用阻塞了,可以继续向下进行了。

从源码里看,wait方法中有参数,也就是不用唤醒谁,只是不再执行wait,向下继续执行而已。

        * 在join()方法中,对于isAlive()和wait()方法的作用对象是个比较让人困惑的问题:

    isAlive()方法的签名是:public final native boolean isAlive(),也就是说isAlive()是判断当前线程的状态,也就是bt的状态。

wait()方法在jdk文档中的解释如下:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor.The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

在这里,当前线程指的是at。

到此这篇关于Thread.join()详解 的文章就介绍到这了,更多相关Thread.join()详解 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring+SpringMVC+Hibernate项目环境搭建的步骤(图文)

    Spring+SpringMVC+Hibernate项目环境搭建的步骤(图文)

    这篇文章主要介绍了Spring+SpringMVC+Hibernate项目环境搭建的步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • 使用Kubernetes和Docker部署Java微服务详细代码

    使用Kubernetes和Docker部署Java微服务详细代码

    Java微服务项目是一种基于Java技术栈的分布式系统开发方式,下面这篇文章主要给大家介绍了关于使用Kubernetes和Docker部署Java微服务的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • spring cloud整合ribbon问题及解决方案

    spring cloud整合ribbon问题及解决方案

    很多小伙伴在整合ribbon都出了相同的问题,今天特地为大家整理了该问题的解决方案,文中有非常详细的图文解说,对出现同样问题的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • JAVA找不到符号的三种解决方案

    JAVA找不到符号的三种解决方案

    这篇文章主要给大家介绍了关于JAVA找不到符号的三种解决方案, 找不到符号错误主要发生在我们试图引用一个未在我们正在编译的程序中声明的变量时,这意味着编译器不知道我们所引用的Java变量,需要的朋友可以参考下
    2024-03-03
  • JavaSwing后台播放音乐mp3

    JavaSwing后台播放音乐mp3

    这篇文章主要为大家详细介绍了JavaSwing后台播放音乐mp3,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • Spring MVC映射HTTP请求到Controller的处理方法

    Spring MVC映射HTTP请求到Controller的处理方法

    我们来详细分析一下如何在 Spring MVC 中将 HTTP 请求映射到 Controller 的处理方法(Handler Methods)上,以及 @RequestMapping 注解的使用方法,需要的朋友可以参考下
    2025-05-05
  • Java案例使用集合方法实现统计任意字符串中字符出现的次数

    Java案例使用集合方法实现统计任意字符串中字符出现的次数

    这篇文章主要介绍了Java案例使用集合方法实现统计任意字符串中字符出现的次数,下面我们将用两种方法实现,需要的小伙伴可以参考一下文章具体内容
    2022-04-04
  • Struts2 $,#,%详解及实例代码

    Struts2 $,#,%详解及实例代码

    这篇文章主要介绍了Struts2 $,#,%详解及实例代码的相关资料,需要的朋友可以参考下
    2016-12-12
  • 解决Mybatis的@Param()注解导致分页失效的问题

    解决Mybatis的@Param()注解导致分页失效的问题

    这篇文章主要介绍了解决Mybatis的@Param()注解导致分页失效的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Spring Boot 注解方式自定义Endpoint详解

    Spring Boot 注解方式自定义Endpoint详解

    这篇文章主要介绍了Spring Boot注解方式自定义Endpoint详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论