浅谈Java线程Thread.join方法解析

 更新时间:2018年01月13日 11:10:32   作者:Ihesong  
本篇文章主要介绍了浅谈Java线程Thread.join方法解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

join字面上是加入的意思,我们先看看join方法的解释和实现。

/**
   * Waits for this thread to die.
   * 调用方线程(调用join方法的线程)执行等待操作,直到被调用的线程(join方法所属的线程)结束,再被唤醒
   * <p> An invocation of this method behaves in exactly the same
   * way as the invocation
   *
   *
   * @throws InterruptedException
   *     if any thread has interrupted the current thread. The
   *     <i>interrupted status</i> of the current thread is
   *     cleared when this exception is thrown.
   */
  public final void join() throws InterruptedException {
    join(0);
  }

这里join是调用的

/**
   * Waits at most {@code millis} milliseconds for this thread to
   * die. A timeout of {@code 0} means to wait forever.
   * 等待线程执行结束,或者指定的最大等待时间到了,调用方线程再次被唤醒,如果最大等待时间为0,则只能等线程执行结束,才能被唤醒。
   * <p> This implementation uses a loop of {@code this.wait} calls
   * conditioned on {@code this.isAlive}. As a thread terminates the
   * {@code this.notifyAll} method is invoked. It is recommended that
   * applications not use {@code wait}, {@code notify}, or
   * {@code notifyAll} on {@code Thread} instances.
   *
   * 
   */
  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;
      }
    }
  }

可以看到,join方法本身是通过wait方法来实现等待的,这里判断如果线程还在运行中的话,则继续等待,如果指定时间到了,或者线程运行完成了,则代码继续向下执行,调用线程就可以执行后面的逻辑了。

但是在这里没有看到哪里调用notify或者notifyAll方法,如果没有调用的话,那调用方线程会一直等待下去,那是哪里调用了唤醒它的方法呢?通过查证得知,原来在线程结束时,java虚拟机会执行该线程的本地exit方法,

//线程退出函数:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
...
//这里会处理join相关的销毁逻辑
ensure_join(this);
...
}
//处理join相关的销毁逻辑
  static void ensure_join(JavaThread* thread) {
   Handle threadObj(thread, thread->threadObj());

   ObjectLocker lock(threadObj, thread);

   thread->clear_pending_exception();

   java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);

   java_lang_Thread::set_thread(threadObj(), NULL);

   //这里就调用notifyAll方法,唤醒等待的线程
   lock.notify_all(thread);

   thread->clear_pending_exception();
  }

这样线程什么时候被唤醒就明白了。下面写个例子看下效果。

public class JoinTest {
  
  public static void main(String[] args) {
    
    ThreadBoy boy = new ThreadBoy();
    boy.start();
    
  }
  
  static class ThreadBoy extends Thread{
    @Override
    public void run() {
      
      System.out.println("男孩和女孩准备出去逛街");
      
      ThreadGirl girl = new ThreadGirl();
      girl.start();
      
      try {
        girl.join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println("男孩和女孩开始去逛街了");
    }
  }
  
  static class ThreadGirl extends Thread{
    @Override
    public void run() {
      int time = 5000;
      
      System.out.println("女孩开始化妆,男孩在等待。。。");
      
      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println("女孩化妆完成!,耗时" + time);
      
    }
  }
  
}

执行结果为:

男孩和女孩准备出去逛街
女孩开始化妆,男孩在等待。。。
女孩化妆完成!,耗时5000
男孩和女孩开始去逛街了

就是男孩和女孩准备去逛街,女孩要化妆先,等女孩化妆完成了,再一起去逛街。

那join(time)的用法是怎么样的呢?

public class JoinTest {
  
  public static void main(String[] args) {
    
    ThreadBoy boy = new ThreadBoy();
    boy.start();
    
  }
  
  static class ThreadBoy extends Thread{
    @Override
    public void run() {
      
      System.out.println("男孩和女孩准备出去逛街");
      
      ThreadGirl girl = new ThreadGirl();
      girl.start();
      
      int time = 2000;
      try {
        girl.join(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println("男孩等了" + time + ", 不想再等了,去逛街了");
    }
  }
  
  static class ThreadGirl extends Thread{
    @Override
    public void run() {
      int time = 5000;
      
      System.out.println("女孩开始化妆,男孩在等待。。。");
      
      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println("女孩化妆完成!,耗时" + time);
      
    }
  }
  
}

这里仅仅把join方法换成了join(time)方法,描述改了点,打印的结果是:

男孩和女孩准备出去逛街
女孩开始化妆,男孩在等待。。。
男孩等了2000, 不想再等了,去逛街了
女孩化妆完成!,耗时5000

男孩等了join(time)中的time时间,如果这个time时间到达之后,女孩所在的线程还没执行完,则不等待了,继续执行后面的逻辑,就是不等女孩了,自己去逛街。

由此看出,join方法是为了比较方便的实现两个线程的同步执行,线程1执行,碰到线程2后,等待线程2执行后,再继续执行线程1的执行,加入的意思现在就比较形象化了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java将RTF转换为PDF格式的实现

    Java将RTF转换为PDF格式的实现

    本文主要介绍了Java将RTF转换为PDF格式的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • 带有@Transactional和@Async的循环依赖问题的解决

    带有@Transactional和@Async的循环依赖问题的解决

    这篇文章主要介绍了带有@Transactional和@Async的循环依赖问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • SpringBoot中的配置类(@Configuration)

    SpringBoot中的配置类(@Configuration)

    这篇文章主要介绍了SpringBoot中的配置类(@Configuration),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Spring Security 自定义资源服务器实践过程

    Spring Security 自定义资源服务器实践过程

    这篇文章主要介绍了Spring Security 自定义资源服务器实践,我们通过自己搭建的授权服务器和资源服务器,完整体验了OAuth2流程,需要的朋友可以参考下
    2022-08-08
  • Elasticsearch写入瓶颈导致skywalking大盘空白

    Elasticsearch写入瓶颈导致skywalking大盘空白

    这篇文章主要为大家介绍了Elasticsearch写入瓶颈导致skywalking大盘空白的解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • spring boot实现图片上传和下载功能

    spring boot实现图片上传和下载功能

    这篇文章主要为大家详细介绍了spring boot实现图片上传和下载功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • Spring 缓存在项目中的使用详解

    Spring 缓存在项目中的使用详解

    Spring 缓存机制,Cache接口为缓存的组件规范定义,包扩缓存的各种操作(添加缓存、删除缓存、修改缓存等),本文给大家介绍Spring 缓存在项目中的使用,感兴趣的朋友一起看看吧
    2025-05-05
  • spring+springmvc整合mabytis时mapper注入失败问题解决方法

    spring+springmvc整合mabytis时mapper注入失败问题解决方法

    这篇文章主要介绍了spring+springmvc整合mabytis时mapper注入失败问题解决方法 ,需要的朋友可以参考下
    2017-08-08
  • Java中的Redis是什么意思

    Java中的Redis是什么意思

    Redis是一个非常强大的工具,它可以用来实现很多有趣的应用,还可以使用Redis来实现分布式锁,这样你就可以在多线程或多进程的环境下同步代码,这篇文章主要介绍了Java中的Redis是什么意思,需要的朋友可以参考下
    2023-08-08
  • java自带排序使用

    java自带排序使用

    这篇文章主要给大家分享了java自带排序使用,该方法是升序排序,方法的内部采用了快排实现,但该方法是 稳定的。下面一起来看看文章的详细介绍吧
    2021-12-12

最新评论