Thread中join()使用
一. 使用方式
Thread thread = new Thread("[test] thread");
thread.start();
thread.join();
二. 为啥要用join()
比如当子线程中业务逻辑处理的时间很长时,那么主线程就会先于子线程提前结束,而如果想要主线程在子线程处理完以后再结束(比如需要子线程中返回的数据),那就可以使用 Thread threadSon.join();
三. 作用
join()上面的解释:
Waits for this thread to die.
当前线程指得是子线程,即阻塞主线程继续执行,直到子线程处理结束;
四. 实例
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
String mainThread = Thread.currentThread().getName();
System.out.println(mainThread + " start..");
ThreadB threadB = new ThreadB();
threadB.start();
threadB.join();
System.out.println(mainThread + " end..");
}
}
public class ThreadB extends Thread {
public ThreadB() {
super("[ThreadB] thread");
}
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start..");
for(int i=0;i<5;i++) {
System.out.println(threadName + " loop at 0" + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(threadName + " end..");
}
}
执行结果:
main start..
[ThreadB] thread start..
[ThreadB] thread loop at 00
[ThreadB] thread loop at 01
[ThreadB] thread loop at 02
[ThreadB] thread loop at 03
[ThreadB] thread loop at 04
[ThreadB] thread end..
main end..
五. 源码解析
jdk中join()的源码:
public final void join() throws InterruptedException {
join(0);
}
主要是接下来的 join(long millis)
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*/
public final synchronized void join(long millis) //注意这里的synchronized
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
//如果millis为0的话,只要线程(上面的子线程ThreadB)还活着,就会调用wait(0)阻塞当前线程,
//一直到threadB执行结束;(wait(0)相当于持有threadB线程的锁然后等待)
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
六. 我的感想
我刚刚是在springboot启动过程中的源码才注意到的():
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
//看到了这里
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
然后跟着context.registerShutdownHook();继续挖掘,直到看到了这个类ApplicationShutdownHooks,它里面的runHooks()方法如下:
static void runHooks() {
Collection<Thread> threads;
synchronized(ApplicationShutdownHooks.class) {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
}
for (Thread hook : threads) {
try {
//看到了这个join()方法
hook.join();
} catch (InterruptedException x) { }
}
}
然后就深入地了解下join()方法,从一个地方拓展到其他地方;
我以前也有过类似需要等待子线程执行完,然后执行主线程的需求,当时应该是直接让主线程等待的。。然后再看去探查指定的指标是否完成……有了这个threadB.join()方法,这样就合理多了,避免了主线程等待时的空自旋;
六. 参考文章
https://blog.csdn.net/sinat_29384657/article/details/52228578
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现