文
章
目
录
章
目
录
本文主要讲解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),学习愉快哦!