线程相关方法之join方法
join()方法作用直观解释就是”等待调用此方法的线程死去“。说得更直观一点就是:在线程 t1 内部调用t2.join(), 那么 t1 将会等待 t2 执行完毕后才会继续执行。(亦可以理解为 t2 加入了 t1, 顺序执行完 t2 的逻辑才轮到 t1 剩下的待执行逻辑)

join()的重载版本
(1)join(long millis),
(2) join(long millis, int nanos)


不带参数的版本和join(0),join(0,0)的效果是一样的【实际上查看jdk源码可以发现join()调用的就是 join(0);而join()方法其实是调用Object的wait方法实现的】,即等待调用线程直至其执行完。显然,有时间参数的重载版本就是指等待相应的时长了。

说再多都是没用的,看个简单的例子。如下代码:
package org.devsong.test;

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyRunnable("A"));
        Thread t2 = new Thread(new MyRunnable("B"));
        t1.start();
        t2.start();
        System.out.println("main");
    }
}

class MyRunnable implements Runnable {

    String str = "";

    public MyRunnable(String str) {
        this.str = str;
    }

    @Override
    public void run() {
        int i = 0;
        System.out.print("[" + str);
        while (i++ < 10) {
            System.out.print(str);
        }
        System.out.println(str + "]");
    }
}
多次执行以上代码,你看到的结果基本都是main输出在最前面,然后AB不规律地输出在后面,类似以下结果:
main
[BBBBBB[AAAAAAAAAAABBBBBA]
B]

那么有什么办法能输出语句在顺序执行呢,即执行完t1 ,再执行t2,最后执行主线程中的输出语句呢?此处join的用处就出现了,将代码作如下修改,在t1.start和t2.start()后面均加上各自的join()方法
package org.devsong.test;

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyRunnable("A"));
        Thread t2 = new Thread(new MyRunnable("B"));
        t1.start();
        t1.join();  //添加t1.join(), 注意此时是在主线程中执行,即在主线程中调用t1的join方法
        t2.start();
        t2.join(); //添加t2.join()
        System.out.println("main");
    }
}

class MyRunnable implements Runnable {

    String str = "";

    public MyRunnable(String str) {
        this.str = str;
    }

    @Override
    public void run() {
        int i = 0;
        System.out.print("[" + str);
        while (i++ < 10) {
            System.out.print(str);
        }
        System.out.println(str + "]");
    }
}
然后再执行上述代码,你会发现,无论执行多少次都是如下结果
[AAAAAAAAAAAA]
[BBBBBBBBBBBB]
main

现在分析为什么会这样。首先明确一下我们调用join方法的位置,可以发现我们是在主线程中调用的 t1 和 t2 的join方法的。并且注意到 t1.start()后面的 t2 相关方法调用也是主线程的一部分。所以 t1.join()后的语句都要等t1死亡后才会得到执行

整个的流程就是: t1.start()->t1.join() ----------> t1执行完毕,t2.start()->t2.join()---------->t2执行完毕,输出"main"

也就是说执行顺序由之前的”抢占式输出“变为了顺序执行(注意此处不是 t2 等待 t1 执行完,而是主线程等待t1执行完毕【因为t2的start(),join()均是在主线程中调用,主线程是主角】,此时 t2 还没启动):

文章正文图片

注:上图只是简单地描述了为什么会出现顺序输出。

下面看一下有时间限制的重载版本是什么情况,将上述代码作简单修改,在 t1.join()中加入参数 3, 为有足够的时长供join()方法”等待“,将run方法内的输出次数限制改为 1000, 如下:
package org.devsong.test;

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyRunnable("A"));
        Thread t2 = new Thread(new MyRunnable("B"));
        t1.start();
        t1.join(3); //等待 t1 执行 3 毫秒,3毫秒后就算 t1 没执行完也接着往下执行主线程中的内容
        t2.start(); //所以最终的结果是 t1 还没执行完就开始执行 t2
        t2.join();
        System.out.println("main");
    }
}

class MyRunnable implements Runnable {

    private String str = "";

    public MyRunnable(String str) {
        this.str = str;
    }

    @Override
    public void run() {
        int i = 0;
        System.out.print("[" + str);
        while (i++ < 1000) {   //为有足够的执行时长供 join 函数演示,此处将循环限制增加到1000
            System.out.print(str);
        }
        System.out.println(str + "]");
    }
}
执行之后会发现,虽然调用了t1.join()方法, 但是由于有 3 毫秒的时间限制,所以 t1 中的内容并未完全输出 t2 就开始运行了

至此,join()方法的简单分析就算完成了。当然,以上只是我个人的看法,描述可能有不严谨或者错误的地方,若您发现了问题,或者有好的想法,欢迎给我留言。
It's
欢迎访问本站,欢迎留言、分享、点赞。愿您阅读愉快!
*转载请注明出处,严禁非法转载。
https://www.devsong.org
QQ留言 邮箱留言
头像
引用:
取消回复
提交
涂鸦
涂鸦
热门