news 2026/4/30 15:56:29

Java面试必看:如何让Main线程成为最后一个退出的秘密!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java面试必看:如何让Main线程成为最后一个退出的秘密!

文章目录

  • Java面试必看:如何让Main线程成为最后一个退出的秘密!
    • 一、问题背景:为什么我们要关心Main线程的退出顺序?
    • 二、常见的误区:为什么直接运行代码会导致Main线程提前退出?
      • 示例代码:
      • 原因分析:
    • 三、解决方案:如何让Main线程等待所有子线程完成?
      • 方法一:使用`join()`方法
        • 修改后的代码:
        • 运行结果:
      • 方法二:使用守护线程
        • 修改后的代码:
        • 运行结果:
      • 方法三:使用线程池和Future
        • 修改后的代码:
        • 运行结果:
      • 方法四:使用CompletableFuture
        • 修改后的代码:
        • 运行结果:
    • 四、总结
    • 希望这篇文章对你理解如何让`Main`线程等待子线程完成有所帮助!
      • 📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!

Java面试必看:如何让Main线程成为最后一个退出的秘密!

大家好,我是闫工!今天我们要聊一个看似简单但其实非常重要的Java面试知识点——如何确保Main线程成为最后一个退出的线程。这个问题在面试中经常被提及,尤其是对于那些有经验或者正在准备进阶的开发者来说。不过别担心,我会用一种轻松幽默的方式带大家一步步弄清楚这个问题。

一、问题背景:为什么我们要关心Main线程的退出顺序?

首先,我们需要明确一个问题:Main线程到底是什么?在Java中,当你运行一个程序时,JVM会默认创建一个主线程(也就是我们常说的Main线程),它负责执行main()方法中的代码。而Main线程的结束就意味着整个程序的结束。

但问题来了:如果我们启动了多个子线程,这些子线程可能在完成任务后继续运行一段时间,甚至可能会在Main线程结束后仍然运行。然而,有时候我们需要确保Main线程是最后一个退出的,尤其是在一些需要优雅关闭或者资源回收的场景中。

举个例子,假设我们有一个程序,它启动了多个后台线程来处理任务,而Main线程负责初始化这些线程并监控它们的状态。如果我们希望Main线程在所有子线程完成任务后才退出,这就需要一些巧妙的线程管理技巧。

二、常见的误区:为什么直接运行代码会导致Main线程提前退出?

很多开发者刚开始接触多线程编程时会发现一个奇怪的现象:他们启动了多个线程,但Main线程却在所有子线程完成任务之前就退出了。为什么会这样呢?让我们通过一个小例子来理解这个问题。

示例代码:

publicclassMainThreadExit{publicstaticvoidmain(String[]args){System.out.println("Main thread is starting...");Threadthread1=newThread(()->{try{Thread.sleep(2000);System.out.println("Thread 1 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});Threadthread2=newThread(()->{try{Thread.sleep(3000);System.out.println("Thread 2 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});thread1.start();thread2.start();System.out.println("Main thread has exited.");}}

运行这段代码,你会看到以下输出:

Main thread is starting... Main thread has exited. Thread 1 has finished! Thread 2 has finished!

从结果可以看到,Main线程在启动两个子线程后立即退出了,而没有等待这两个子线程完成任务。为什么会这样呢?

原因分析:

简单来说,这是因为Main线程只是启动了这些子线程,并没有主动等待它们完成。一旦Main线程执行完自己的代码(也就是打印出“Main thread has exited.”),它就会自动退出。而此时的两个子线程仍然在后台运行。

三、解决方案:如何让Main线程等待所有子线程完成?

既然问题出现在Main线程没有等待子线程完成,那我们就要想办法让Main线程主动等待这些子线程完成任务后再退出。接下来,我会介绍几种常见的方法来实现这一点。

方法一:使用join()方法

最直接的方法就是使用Thread类的join()方法。这个方法的作用是让当前线程(这里是Main线程)等待调用join()的线程完成后再继续执行。

修改后的代码:
publicclassMainThreadExit{publicstaticvoidmain(String[]args){System.out.println("Main thread is starting...");Threadthread1=newThread(()->{try{Thread.sleep(2000);System.out.println("Thread 1 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});Threadthread2=newThread(()->{try{Thread.sleep(3000);System.out.println("Thread 2 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});thread1.start();thread2.start();// 等待子线程完成try{thread1.join();thread2.join();}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("Main thread has exited.");}}
运行结果:
Main thread is starting... Thread 1 has finished! Thread 2 has finished! Main thread has exited.

从结果可以看到,Main线程确实在所有子线程完成后才退出了。这种方法简单直接,但也有它的缺点:当子线程很多时,逐一调用join()可能会显得有些笨拙。

方法二:使用守护线程

另一种方法是将子线程设置为守护线程(Daemon Thread)。守护线程的特点是它不会阻止JVM的退出。换句话说,如果所有的非守护线程都已经退出,那么守护线程会自动退出。

修改后的代码:
publicclassMainThreadExit{publicstaticvoidmain(String[]args){System.out.println("Main thread is starting...");Threadthread1=newThread(()->{try{Thread.sleep(2000);System.out.println("Thread 1 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});Threadthread2=newThread(()->{try{Thread.sleep(3000);System.out.println("Thread 2 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});// 将子线程设置为守护线程thread1.setDaemon(true);thread2.setDaemon(true);thread1.start();thread2.start();System.out.println("Main thread has exited.");}}
运行结果:
Main thread is starting... Main thread has exited. Thread 1 has finished! Thread 2 has finished!

从结果可以看到,Main线程在打印“Main thread has exited.”后立即退出了,而两个子线程仍然继续运行。但需要注意的是,守护线程并不能保证在所有情况下都能优雅地退出,因为它们可能会被JVM强制终止。

方法三:使用线程池和Future

如果你使用的是线程池来管理线程,那么可以通过Future对象来等待所有任务完成后再退出。

修改后的代码:
importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Future;publicclassMainThreadExit{publicstaticvoidmain(String[]args)throwsException{System.out.println("Main thread is starting...");ExecutorServiceexecutor=Executors.newFixedThreadPool(2);Future<?>future1=executor.submit(()->{try{Thread.sleep(2000);System.out.println("Thread 1 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});Future<?>future2=executor.submit(()->{try{Thread.sleep(3000);System.out.println("Thread 2 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});// 等待所有任务完成future1.get();future2.get();executor.shutdown();System.out.println("Main thread has exited.");}}
运行结果:
Main thread is starting... Thread 1 has finished! Thread 2 has finished! Main thread has exited.

这种方法比逐一调用join()更优雅,特别是在处理大量线程时。

方法四:使用CompletableFuture

如果你在Java 8及以上版本中工作,可以考虑使用CompletableFuture来简化代码。

修改后的代码:
importjava.util.concurrent.CompletableFuture;publicclassMainThreadExit{publicstaticvoidmain(String[]args)throwsException{System.out.println("Main thread is starting...");CompletableFuture<Void>future1=CompletableFuture.runAsync(()->{try{Thread.sleep(2000);System.out.println("Thread 1 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});CompletableFuture<Void>future2=CompletableFuture.runAsync(()->{try{Thread.sleep(3000);System.out.println("Thread 2 has finished!");}catch(InterruptedExceptione){e.printStackTrace();}});// 等待所有CompletableFuture完成CompletableFuture.allOf(future1,future2).join();System.out.println("Main thread has exited.");}}
运行结果:
Main thread is starting... Thread 1 has finished! Thread 2 has finished! Main thread has exited.

这种方法不仅代码简洁,而且支持更复杂的异步操作。

四、总结

通过以上几种方法,我们都可以实现让Main线程在所有子线程完成后才退出。选择哪种方法取决于具体的场景和需求:

  • 如果需要简单直接的控制,可以使用join()方法。
  • 如果是大规模的任务处理,建议使用线程池结合FutureCompletableFuture来管理任务。

希望这篇文章对你理解如何让Main线程等待子线程完成有所帮助!

📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!

成体系的面试题,无论你是大佬还是小白,都需要一套JAVA体系的面试题,我已经上岸了!你也想上岸吗?

闫工精心准备了程序准备面试?想系统提升技术实力?闫工精心整理了1000+ 套涵盖前端、后端、算法、数据库、操作系统、网络、设计模式等方向的面试真题 + 详细解析,并附赠高频考点总结、简历模板、面经合集等实用资料!

✅ 覆盖大厂高频题型
✅ 按知识点分类,查漏补缺超方便
✅ 持续更新,助你拿下心仪 Offer!

📥免费领取👉 点击这里获取资料

已帮助数千位开发者成功上岸,下一个就是你!✨

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 6:26:39

YOLO模型失败案例复盘:一次因数据偏差导致的事故

YOLO模型失败案例复盘&#xff1a;一次因数据偏差导致的事故 在某电子制造工厂的一条SMT生产线上&#xff0c;自动化质检系统突然“失明”——连续三天未能识别出一批存在明显电容缺失的PCB板。这些本应被拦截的不良品最终流入后续工序&#xff0c;造成数千元损失和客户投诉。而…

作者头像 李华
网站建设 2026/4/30 3:07:28

YOLO目标检测API设计规范:构建易用服务接口的原则

YOLO目标检测API设计规范&#xff1a;构建易用服务接口的原则 在智能制造、智慧城市和自动驾驶等前沿领域&#xff0c;视觉感知正从“可有可无”走向“核心驱动”。面对海量视频流与实时决策需求&#xff0c;如何将强大的AI模型转化为稳定可靠的服务能力&#xff0c;成为工程落…

作者头像 李华
网站建设 2026/4/29 2:34:30

工程实践:破解智能体错误的长尾效应——论“悔改机制”中的通知分级与防再犯设计

在真实业务里&#xff0c;智能体最危险的失败模式往往不是“当场答错”——因为当场答错至少还有机会被用户质疑、被客服兜底、被人工复核流程拦住。更隐蔽、也更具破坏性的情况是&#xff1a;智能体在某一次会话里给出了看似可信的建议&#xff0c;用户照做了&#xff0c;流程…

作者头像 李华
网站建设 2026/4/30 1:43:36

YOLO模型安全防护指南:防止恶意输入攻击的实践建议

YOLO模型安全防护指南&#xff1a;防止恶意输入攻击的实践建议 在智能制造车间的视觉质检线上&#xff0c;一台搭载YOLOv8的边缘设备突然开始将所有缺陷产品标记为“合格”——调查发现&#xff0c;攻击者通过监控摄像头注入了一组经过精心扰动的图像&#xff0c;成功欺骗了检测…

作者头像 李华
网站建设 2026/4/28 10:47:32

YOLO模型加密保护方案:防止知识产权泄露的措施

YOLO模型加密保护方案&#xff1a;防止知识产权泄露的措施 在智能制造、自动驾驶和智能安防等领域&#xff0c;AI模型正从“技术实验品”快速演变为企业核心竞争力的关键组成部分。尤其是像YOLO这类高性能实时目标检测模型&#xff0c;其训练成本高昂、调优过程复杂&#xff0c…

作者头像 李华
网站建设 2026/4/29 1:56:05

2025年大模型架构演变全解析:从GPT到DeepSeek-V3,万字干货必收藏!

文章详细分析了2025年主流开源大模型架构演变&#xff0c;包括DeepSeek-V3/R1、OLMo 2、Gemma 3、Llama 4等。重点探讨了多头潜在注意力(MLA)、混合专家(MoE)、滑动窗口注意力等创新设计&#xff0c;以及归一化层放置等差异。这些优化在保持模型性能的同时&#xff0c;显著提升…

作者头像 李华