Java线程join方法详解

后端 潘老师 3个月前 (11-25) 67 ℃ (0) 扫码查看

本文主要讲解Java线程join方法相关的内容,我们来一起学习下吧!

为什么需要 join方法?

我们先来看下下面的代码执行,我们希望主线程能获取到t1线程修改r之后的值,大家思考下,以下代码打印 r 是什么呢?

package com.panziye.thread;

import java.util.concurrent.TimeUnit;

public class ThreadJoin {
    static int r = 0;
    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        System.out.println("test1 开始");
        Thread t1 = new Thread(() -> {
            System.out.println("t1 开始");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 结束");
            r = 10;
        });
        // 启动
        t1.start();
        System.out.println("结果为:"+ r);
        System.out.println("test1 结束");
    }
}

运行发现,输出结果如下:

test1 开始
结果为:0
test1 结束
t1 开始
t1 结束

分析

  • 因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10
  • 而主线程一开始就要打印 r 的结果,所以只能打印出 r=0

解决方法

  • 用 sleep 行不行?为什么?
  • 用 join,加在 t1.start() 之后即可

首先主线程用sleep等待t1线程执行完,再去打印r值是可以实现r=10的情况,但是你难以预计sleep的时长,也就是无法预估t1执行的时长,因此不推荐,从而可以是join方法来实现,我们只需要在t1.start()方法后调用t1.join()方法即可等待t1运行结束:

private static void test1() throws InterruptedException {
    System.out.println("test1 开始");
    Thread t1 = new Thread(() -> {
        System.out.println("t1 开始");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 结束");
        r = 10;
    });
    // 启动
    t1.start();
    // 等待t1运行结束
    t1.join();
    System.out.println("结果为:"+ r);
    System.out.println("test1 结束");
}

由于join方法会抛出中断异常,因此方法也声明抛出即可,再运行测试:

test1 开始
t1 开始
t1 结束
结果为:10
test1 结束

从打印的执行顺序我们也可以发现当调用join方法后,会等待t1线程执行完再去继续执行test1的后续代码,这就是join方法的作用。

join(long millis)方法

join有个重载带参数的方法,参数是等待毫秒值,意思就是如果超过设置的毫秒值线程还没有运行完就不再继续等待,直接继续运行下面的代码,我们可以测试下,我们将上面的代码修改如下:

private static void test1() throws InterruptedException {
    System.out.println("test1 开始");
    Thread t1 = new Thread(() -> {
        System.out.println("t1 开始");
        try {
            // 休眠10s
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 结束");
        r = 10;
    });
    // 启动
    t1.start();
    // 等待t1运行结束,超过2s没结束就不再等待
    t1.join(2000);
    System.out.println("结果为:"+ r);
    System.out.println("test1 结束");
}

我们让t1线程运行10s,而join只等待2s,我们会发现直接t1没结束就运行下面的方法了:

test1 开始
t1 开始
结果为:0
test1 结束
t1 结束

join方法源码

为了更方便大家理解join方法,其实我们看下join方法源码就会更清楚了:

public final void join() throws InterruptedException {
        join(0);
}

无参的join就是调用的有参的join:

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()方法,其实就是走if (millis == 0)部分逻辑,就是while循环遍历判断线程是否还活着,如果活着则继续等待,一旦运行结束,isAlive也变成了false,就结束等待,跳出循环了,从而继续执行主线程后续代码了。

以上就是Java线程join方法详解的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/11836.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】