浅析 Thread.join()

概要

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

1. join() 的示例和作用

2. join() 源码分析

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

 

1. join() 的示例和作用

1.1 示例

复制代码
// 主线程
public class Parent extends Thread {
    public void run() {
        Child child = new Child();
        child .start();
        child .join();
        // ...
    }
}

// 子线程
public class Child extends Thread {
    public void run() {
        // ...
    }
}
复制代码

上面代码中有两个类:Parent(主线程类),Child(子线程类)。

在 Parent.run() 中,通过 new Child() 新建 child 子线程(此时 child 处于 NEW 状态),然后调用 child.start()(child 转换为 RUNNABLE 状态),再调用 child.join()。

在 Parent 调用 child.join() 后,child 子线程正常运行,Parant 主线程会等待 child 子线程结束后再继续运行。

 

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 may have a program that will begin initializing the resources it needs before proceeding with the rest of the execution. We can run the initialization tasks as threads and wait for its finalization before continuing with the rest of the program. For this purpose, we can use the join() method of the Thread class. When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.

 

2. join() 源码分析

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

复制代码
 1 public final void join() throws InterruptedException {
 2     join(0);
 3 }
 4 
 5 public final synchronized void join(long millis)
 6 throws InterruptedException {
 7     long base = System.currentTimeMillis();
 8     long now = 0;
 9 
10     if (millis < 0) {
11         throw new IllegalArgumentException("timeout value is negative");
12     }
13 
14     if (millis == 0) {
15         while (isAlive()) {
16             wait(0);
17         }
18     } else {
19         while (isAlive()) {
20             long delay = millis - now;
21             if (delay <= 0) {
22                 break;
23             }
24             wait(delay);
25             now = System.currentTimeMillis() - base;
26         }
27     }
28 }
29 
30 public final synchronized void join(long millis, int nanos)
31 throws InterruptedException {
32 
33     if (millis < 0) {
34         throw new IllegalArgumentException("timeout value is negative");
35     }
36 
37     if (nanos < 0 || nanos > 999999) {
38         throw new IllegalArgumentException(
39                             "nanosecond timeout value out of range");
40     }
41 
42     if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
43         millis++;
44     }
45 
46     join(millis);
47 }
复制代码

我们可以看到 join() 一共有三个版本:

1 public final void join();
2 
3 public final synchronized void join(long millis);
4 
5 public final synchronized void join(long millis, int nanos);

其中

a. join() 和 join(long millis, int nanos) 最后都调用了 join(long millis)。

b. 带参数的 join() 都为 synchronized method。

c. join() 调用了 join(0),从源码可以看到 join(0) 不断检查线程(join() 所属的线程实例,非调用线程)是否是 Active。

 

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

Parent 调用 child.join(),child.join() 再调用 child.join(0) (此时 Parent 会获得 child 实例作为锁,其他线程可以进入 child.join() ,但不可以进入 child.join(0), 因为没有获得锁),child.join(0) 会不断地检查 child 线程是否是 Active。

如果是 Active,则不断地调用 child.wait(0)(此时 Parent 会释放 child 实例锁,其他线程可以竞争锁并进入 child.join(0))。我们可以得知,Parent 线程在不断地对 child.wait(0) 入栈和出栈。

一旦 child 线程不为 Active (状态为 TERMINATED), child.join(0) 会直接返回到 child.join(), child.join() 会直接返回到 Parent 主线程,Parent 主线程就可以继续运行下去了。

 

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

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

 

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

“唤醒”令人误解。其实是主线程调用方法不断去检查子线程的状态, 这是个主动的动作,而不是子线程去唤醒主线程

 

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

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

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

 

一点感想:技术人员写作技术文章时,最好尽量避免使用过于口语化的词汇。因为这种词汇歧义比较大,会让读者感到更加困惑或形成错误的理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值