Java基础填坑日志(2)——Thread.join()方法使用分析

前言

在学习java中的并发的时候,很容易注意到一个函数join,此函数的功能是等待指定的线程死亡或者经过指定的毫秒数,如果不指定毫秒数或者指定的毫秒数为空,则一直等待直到指定的线程死亡。JDK源码如下:

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <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.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @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 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()就等价于join(0)join函数的功能挺简单,不过在学习查阅资料的过程中,发现在等待的过程中,会影响到其它线程的工作状态,所以自己写了一个小demo测试了一下,最后总结出了join更深层次的含义。

测试join函数

在下面的小demo中,我写了两个测试的线程,分别开启线程,观察输出。

public class JoinTest {
    public static void main(String[] args) {
        System.out.println("主线程开始执行");

        Runnable r1 = () -> {
            System.out.println("线程1开始执行");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {

            }
            System.out.println("线程1结束执行");
        };

        Runnable r2 = () -> {
            System.out.println("线程2开始执行");
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {

            }
            System.out.println("线程2结束执行");
        };

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
        System.out.println("主线程结束执行");
    }
}
----------输出结果----------
主线程开始执行
主线程结束执行
线程1开始执行
线程2开始执行
线程2结束执行
线程1结束执行
----------分析----------
主线程开始->线程1、线程2随机某个时点开始执行
(可能在主线程结束后,线程12结束时间点由开始时间和运行时长决定)->主线程结束执行

对于上面的demo,可以很明显的看出,在主线程执行的时候,开启了两个新线程,但是两个新线程的开始执行时间是随机的,而结束时间我人为设置了线程2会执行得更快。

public class JoinTest {
    public static void main(String[] args) {
        System.out.println("主线程开始执行");
        ......
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        try {
            t1.join();
        } catch (InterruptedException e) {

        }
        t2.start();
        System.out.println("主线程结束执行");
    }
}
----------输出结果----------
主线程开始执行
线程1开始执行
线程1结束执行
主线程结束执行
线程2开始执行
线程2结束执行
----------分析----------
主线程开始->线程1随机某个时点开始执行->线程1结束执行->
主线程结束执行->线程2随机某个时点开始执行(可能在主线程结束前)->线程2结束执行

当主线程调用线程1的join()方法,主线程进入等待状态,直到线程1运行结束,主线程才由等待状态转为可执行状态,所以一定是在线程1结束后主线程才能结束执行。

public class JoinTest {
    public static void main(String[] args) {
        System.out.println("主线程开始执行");
        ......
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
        try {
            t2.join();
        } catch (InterruptedException e) {

        }
        System.out.println("主线程结束执行");
    }
}
----------输出结果----------
主线程开始执行
线程1开始执行
线程2开始执行
线程2结束执行
主线程结束执行
线程1结束执行
----------分析----------
主线程开始->线程1、线程2随机某个时点开始执行
(可能在主线程结束后,线程12结束时间点由开始时间和运行时长决定)->
线程2结束执行->主线程结束执行

同理,当主线程调用线程2的join()方法,主线程进入等待状态,直到线程2运行结束,主线程才由等待状态转为可执行状态,所以一定是在线程2结束后主线程才能结束执行。

总结

A线程中,使用B.join()函数后会等待指定的线程死亡,然后继续运行,如果有其它线程C其执行规则不变。

参考

多线程中join()
Thread源码分析之join方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章