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

 更新时间:2020-08-01 06:25:16   作者:佚名   我要评论(0)

概要
本文分三个部分对Thread.join()进行分析:
1. join() 的示例和作用
2. join() 源码分析
3. 对网上其他分析 join() 的文章提出疑问
1. join() 的示例和作用
1.1

概要

本文分三个部分对Thread.join()进行分析:

1. join() 的示例和作用

2. join() 源码分析

3. 对网上其他分析 join() 的文章提出疑问

1. join() 的示例和作用

1.1 示例

// 父线程
public class Parent {
 public static void main(String[] args) {
  // 创建child对象,此时child表示的线程处于NEW状态
  Child child = new Child();
  // child表示的线程转换为RUNNABLE状态
  child.start();
  // 等待child线程运行完再继续运行
  child.join();
 }
}
// 子线程
public class Child extends Thread {
 public void run() {
  // ...
 }
}

上面代码展示了两个类:Parent(父线程类),Child(子线程类)。

Parent.main()方法是程序的入口,通过Child child = new Child(); 新建child子线程(此时 child子线程处于NEW状态);

然后调用child.start()(child子线程状态转换为RUNNABLE);

再调用child.join(),此时,Parent父线程会等待child子线程运行完再继续运行。

下图是我总结的 Java 线程状态转换图:

1.2 join() 的作用

让父线程等待子线程结束之后才能继续运行。

我们来看看在 Java 7 Concurrency Cookbook 中相关的描述(很清楚地说明了 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.

当我们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。

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() 一共有三个重载版本,分别是无参、一个参数、两个参数:

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)无参版本和两个参数版本最终都调用了一个参数的版本。

(3) join() 和 join(0) 是等价的,表示一直等下去;join(非0)表示等待一段时间。

从源码可以看到 join(0)调用了Object.wait(0),其中Object.wait(0)会一直等待,直到被notify/中断才返回。

while(isAlive())是为了防止子线程伪唤醒(spurious wakeup),只要子线程没有TERMINATED的,父线程就需要继续等下去。

(4) join() 和 sleep() 一样,可以被中断(被中断时,会抛出 InterrupptedException 异常);不同的是,join() 内部调用了 wait(),会出让锁,而 sleep() 会一直保持锁。

以本文开头的代码为例,我们分析一下代码逻辑:

调用链:Parent.main() -> child.join() -> child.join(0) -> child.wait(0)(此时 Parent线程会获得 child 实例作为锁,其他线程可以进入 child.join() ,但不可以进入 child.join(0), 因为child.join(0)是同步方法)。

如果 child 线程是 Active,则调用 child.wait(0)(为了防止子线程 spurious wakeup, 需要将 wait(0) 放入while(isAlive())循环中。

一旦 child 线程不为 Active (状态为 TERMINATED),child.notifyAll()会被调用-> child.wait(0)返回 -> child.join(0)返回 -> child.join()返回 -> Parent.main()继续执行, 子线程会调用this.notify(),child.wait(0)会返回到child.join(0) ,child.join(0)会返回到 child.join(), child.join() 会返回到 Parent 父线程,Parent 父线程就可以继续运行下去了。

3. 对网上其他分析 join() 的文章提出疑问

我觉得网上很多文章的描述有歧义,下面挑选一些描述进行分析,也欢迎大家留言一起讨论。

a. 子线程结束之后,"会唤醒主线程",父线程重新获取cpu执行权,继续运行。

这里感谢kerwinX的留言,子线程结束后,子线程的this.notifyAll()会被调用,join()返回,父线程只要获取到锁和CPU,就可以继续运行下去了。

b. join() 将几个并行的线程"合并为一个单线程"执行。

我理解这个说法的意思,但是这样描述只会让读者更难理解。

在调用 join() 方法的程序中,原来的多个线程仍然多个线程,并没有发生“合并为一个单线程”。真正发生的是调用join() 的线程进入 TIMED_WAITING 状态,等待 join() 所属线程运行结束后再继续运行。

一点感想:技术人员写作技术文章时,最好尽量避免使用过于口语化的词汇。

因为这种词汇歧义比较大,会让读者感到更加困惑或形成错误的理解。

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

您可能感兴趣的文章:

  • java 中Thread.join()的使用方法
  • JAVA CountDownLatch与thread-join()的区别解析
  • Java线程编程中isAlive()和join()的使用详解

相关文章

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

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

    概要 本文分三个部分对Thread.join()进行分析: 1. join() 的示例和作用 2. join() 源码分析 3. 对网上其他分析 join() 的文章提出疑问 1. join() 的示例和作用 1.1
    2020-08-01
  • Python使用shutil模块实现文件拷贝

    Python使用shutil模块实现文件拷贝

    主要作用与拷贝文件用的。 1.shutil.copyfileobj(文件1,文件2):将文件1的数据覆盖copy给文件2。 import shutil f1 = open("1.txt",encoding="utf-8") f2 = o
    2020-08-01
  • vue+vuex+axios从后台获取数据存入vuex,组件之间共享数据操作

    vue+vuex+axios从后台获取数据存入vuex,组件之间共享数据操作

    在vue项目中组件间相互传值或者后台获取的数据需要供多个组件使用的情况很多的话,有必要考虑引入vuex来管理这些凌乱的状态,今天这边博文用来记录这一整个的过程,
    2020-08-01
  • Java 操作Properties配置文件详解

    Java 操作Properties配置文件详解

    java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是"键=值"的格式,在properties 文件
    2020-08-01
  • Python如何执行精确的浮点数运算

    Python如何执行精确的浮点数运算

    问题 你需要对浮点数执行精确的计算操作,并且不希望有任何小误差的出现。 解决方案 浮点数的一个普遍问题是它们并不能精确的表示十进制数。 并且,即使是最简单
    2020-08-01
  • Python高并发解决方案实现过程详解

    Python高并发解决方案实现过程详解

    一.cdn加速 简单说就是把静态资源放到别人服务器上 全称:Content Delivery Network或Content Ddistribute Network,即内容分发网络 基本思路: 尽可能避开互联
    2020-08-01
  • Python 如何测试文件是否存在

    Python 如何测试文件是否存在

    问题 你想测试一个文件或目录是否存在。 解决方案 使用 os.path 模块来测试一个文件或目录是否存在。比如: >>> import os >>> os.path.exists('/etc/passwd') Tr
    2020-08-01
  • Django+Uwsgi+Nginx如何实现生产环境部署

    Django+Uwsgi+Nginx如何实现生产环境部署

    如何在生产上部署Django&#63; Django的部署可以有很多方式,采用nginx+uwsgi的方式是其中比较常见的一种方式。 uwsgi介绍 uWSGI是一个Web服务器,它实现了WSG
    2020-08-01
  • Java日志框架之logback使用详解

    Java日志框架之logback使用详解

    为什么使用logback 记得前几年工作的时候,公司使用的日志框架还是log4j,大约从16年中到现在,不管是我参与的别人已经搭建好的项目还是我自己主导的项目,日志框架
    2020-08-01
  • Python打印不合法的文件名

    Python打印不合法的文件名

    问题 你的程序获取了一个目录中的文件名列表,但是当它试着去打印文件名的时候程序崩溃, 出现了 UnicodeEncodeError 异常和一条奇怪的消息—— surrogates not all
    2020-08-01

最新评论