Thread的join方法
Thread的join方法
关于join官方的解释是 Waits for this thread to die. 也就是等待一个线程结束。
我们来先来一段代码来引入join的使用场景(这里使用了java8的IntStream)
/**
* @program: ThreadDemo
* @description: 正常两个线程交替执行
* @author: hs96.cn@Gmail.com
* @create: 2020-09-03
*/
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}, "t1");
t1.start();
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}
}
运行结果如下 :
可以看到正常两个线程是交替执行的。如果我们想线程t1执行完再执行main线程呢,这里就需要使用join了:
/**
* @program: ThreadDemo
* @description: 使用join方法线程t1结束后才会执行线程main
* @author: hs96.cn@Gmail.com
* @create: 2020-09-03
*/
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
//java8 IntStream
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}, "t1");
t1.start();
t1.join();
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}
}
运行结果如下:
再增加一个子线程,join一下试试:
/**
* @program: ThreadDemo
* @description: 使用join方法线程t1结束后才会执行线程main
* @author: hs96.cn@Gmail.com
* @create: 2020-09-03
*/
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
//java8 IntStream
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}, "t1");
Thread t2 = new Thread(() -> {
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
IntStream.range(1, 1_000).forEach(i ->
System.out.println(Thread.currentThread().getName() + "->" + i)
);
}
}
结果和我们的预期一样,两个子线程执行完之后才执行main线程。
join方法还支持几个参数:
一个毫秒级别,一个微妙级别,就是最多等待这个线程执行多久。
代码来测试一下:
/**
* @program: ThreadDemo
* @description: join(long millis), join(long millis, int nanos)
* @author: hs96.cn@Gmail.com
* @create: 2020-09-03
*/
public class ThreadJoin2 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
System.out.println("t1 is running...");
Thread.sleep(5_000);
System.out.println("t1 is done.");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
//只等待t1线程3秒
t1.join(3_000);
System.out.println("main thread is running...");
}
}
运行效果如下:
流程如下:程序开始进入t1线程,开始执行t1线程run方法,t1线程run方法打印t1 is running…,然后开始sleep5秒,这时候程序执行到t1.join(3_000);也就是只等待t1线程3秒,等待结束后main线程打印main thread is running…,再过大约2秒,t1sleep结束,打印t1 is done。后面的我们就不测试了,现在引出一个新的问题:
一些嵌入式的HTTP Server,比如jetty,为什么把任务启动,一会之后会自动挂掉?其实原因很简单就是在主线程退出之后会把http server挂掉(守护线程),避免占用端口、浪费资源。解决办法是使用Thread.currentThread().join();。让当前线程执行,直到当前线程死掉。
测试代码如下:
/**
* @program: ThreadDemo
* @description: join(long millis), join(long millis, int nanos)
* @author: hs96.cn@Gmail.com
* @create: 2020-09-03
*/
public class ThreadJoin2 {
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().join();
}
}
运行效果如下:
可以看到currentThread一直在等待,thread done….永远不会输出出来。
接下来结合一个案例感受一下join在多线程中的使用场景:比如我们启动多个线程来采集服务器节点的信息,那么我们该如何保证唯一的采集结束时间呢?
/**
* @program: ThreadDemo
* @description: 采集服务器节点的信息的例子。问题:多个线程如何得到唯一的采集结束时间?
* @author: hs96.cn@Gmail.com
* @create: 2020-09-03
*/
public class ThreadJoin3 {
public static void main(String[] args) throws InterruptedException {
long startTimestamp = System.currentTimeMillis();
// 假设有三台机器,开启三个线程。
Thread m1 = new Thread(new CaptureRunnable("M1", 1_000L));
Thread m2 = new Thread(new CaptureRunnable("M2", 2_000L));
Thread m3 = new Thread(new CaptureRunnable("M3", 3_000L));
m1.start();
m2.start();
m3.start();
long endTimestamp = System.currentTimeMillis();
System.out.printf("Save data begin timestamp is %s, end timestamp is %s\n", startTimestamp, endTimestamp);
System.out.printf("Spend time is %s", endTimestamp - startTimestamp);
}
}
/**
* 采集服务器节点的任务。
*/
class CaptureRunnable implements Runnable {
// 机器节点的名称
private String machineName;
// 采集花费时间
private long spendTime;
public CaptureRunnable(String machineName, long spendTime) {
this.machineName = machineName;
this.spendTime = spendTime;
}
@Override
public void run() {
// do the really capture data.
try {
Thread.sleep(spendTime);
System.out.printf(machineName + " completed data capture at timestamp [%s] and successful.\n", System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getResult() {
return machineName + " finish.";
}
}
运行效果如下:
可以看到三个线程还没走完,就提前把时间打印出来了,这个不是我我们想要的效果,那么我们让三个线程join一下试试:
// 假设有三台机器,开启三个线程。
Thread m1 = new Thread(new CaptureRunnable("M1", 1_000L));
Thread m2 = new Thread(new CaptureRunnable("M2", 2_000L));
Thread m3 = new Thread(new CaptureRunnable("M3", 3_000L));
m1.start();
m2.start();
m3.start();
m1.join();
m2.join();
m3.join();
运行效果如下:
这样就达到我们想要的效果了。