面试官:Java中Thread的join方法为什么能让线程插队?

JAVA herman 342浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:codedq,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:codedq,之前的微信号好友位已满,备注:返现
饿了么大量招人,我内推!Java 方向!薪资不设上限,工作年龄不限!工作地点限魔都,可电话面试!简历,发我微信:codedq
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

这个问题很多高级工程师可能都不会,因为平时很少用到。

但是在面试中就有可能经常会遇到这样的问题:在主线程中有两个子线程,如何能让着两个子线程能顺序的执行? 答案自然是用 join 来使得两个线程顺序执行。说到这里,我前面也有类似的文章,最近又有粉丝问到,所以还是推荐大家看一看。

今天这个问题,我们再来通过代码来搞懂它!

public class JoinThread {
    public static void main(String[] args) throws Exception {
        Thread codedq = new MyThread("业余草");
        Thread xttblog = new MyThread("公众号");
        codedq.start();
        codedq.join();
        xttblog.start();
    }
    @Data
    static class MyThread extends Thread {
        private String userName;
        public MyThread(String userName) {
            this.userName = userName;
        }
        @Override
        public void run() {
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(userName + " - " + i);
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

当每个线程启动后,分别打印五次信息,通过不同的名字来区分是哪个线程打印的。最终的执行结果如下:

业余草 - 0
业余草 - 1
业余草 - 2
业余草 - 3
业余草 - 4
公众号 - 0
公众号 - 1
公众号 - 2
公众号 - 3
公众号 - 4

通过最终的结果可以看到 join 可以使得两个线程是顺序执行,那为什么 join 能控制线程顺序执行呢,我们看下 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;
        }
    }
}

通过源码可知,join 通过 synchronized 关键字来保证线程安全,主线程在调用了 codedq.start() 之后调用了 codedq.join(),当 codedq 线程未执行完成时,主线程会被以下代码阻塞。

if (millis == 0) {//join()方法默认milis为0
    while (isAlive()) {//线程未执行完成,此条件为true
        wait(0);//等待notify
    }
}

当 codedq 线程执行完成之后,此线程的生命周期即将结束,在生命周期结束前,codedq 线程会使用 notifyAll() 方法,通知所有正在等待该对象锁的线程(我即将死去,你们不要再等了)。wait(0) 接收到 notify 之后,会再次进行 isAlive() 判断,codedq 死亡之后,就跳出循环,join 方法结束,之后就继续执行主线程中的其他代码。

同时我们也能看到 join 方法里面能传递时间参数,大概作用就是等待指定时间之后,如果之前线程还未执行完成,那么久不再等待。

综上所述,这个面试题并不难。主要难在两点,一是 join 这个方法,平时不常用;二是,就算我们用过 join,但却很少有人去剖析它的源码,思考它的底层实现。

这篇文章有粉丝在面试中遇到,刚好今天周末,撸一篇文章大家共勉,以后再有人遇到此类问题,我就可以把这篇文章甩给他了。如果你也有卡壳的问题,不妨私信我,在空闲时间里为你排忧解难!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:xttblog(5000人已满),微信号3:codedq(超3800)。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 面试官:Java中Thread的join方法为什么能让线程插队?