news 2026/2/28 21:34:16

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/2/13 5:12:30

西门子1200立库机器人码垛机伺服视觉AGV程序大揭秘

西门子1200立库机器人码垛机伺服视觉AGV程序 包括2台西门子PLC1215程序和2台西门子触摸屏TP700程序 PLC与工业相机视觉定位及机器人使用Modbus TCP通讯 PLC和码垛机Modbus TCP通讯&#xff08;SCL语言&#xff09; PLC和4台G120变频使用Profinet通讯 1个伺服轴&#xff0c;AGV …

作者头像 李华
网站建设 2026/2/25 0:34:19

基于 MATLAB 的一维数据二分类

基于MATLAB的一维数据二分类在数据分析和机器学习的世界里&#xff0c;二分类问题是最基础也是最常见的任务之一。今天咱们就来聊聊如何使用 MATLAB 对一维数据进行二分类。 问题背景 假设我们有一组一维的数据&#xff0c;这些数据可以是各种测量值&#xff0c;比如温度、压力…

作者头像 李华
网站建设 2026/2/18 1:14:39

基于主从博弈理论的共享储能与综合能源微网优化运行研究

MATLAB代码&#xff1a;基于主从博弈理论的共享储能与综合能源微网优化运行研究 关键词&#xff1a;主从博弈 共享储能 综合能源微网 优化调度 参考文档&#xff1a;《基于主从博弈理论的共享储能与综合能源微网优化运行研究》完全复现 仿真平台&#xff1a;MATLAB yalmipcple…

作者头像 李华
网站建设 2026/2/21 10:40:59

YOLO模型训练成本太高?试试我们的低成本高性能算力方案

YOLO模型训练成本太高&#xff1f;试试我们的低成本高性能算力方案 在智能制造工厂的质检线上&#xff0c;一台搭载AI视觉系统的机械臂正高速运转——它需要在毫秒级时间内识别出电路板上的微小焊点缺陷。这类对实时性与精度双高要求的任务&#xff0c;如今大多由YOLO系列模型驱…

作者头像 李华
网站建设 2026/2/21 12:16:22

YOLO实时性背后的秘密:浅析网格预测与锚框机制

YOLO实时性背后的秘密&#xff1a;浅析网格预测与锚框机制 在智能制造车间的一条高速SMT贴片线上&#xff0c;每分钟有数百块PCB板流过检测工位。摄像头捕捉图像后&#xff0c;系统必须在15毫秒内完成缺陷识别——是虚焊、错件还是缺件&#xff1f;任何延迟都会导致整条产线停摆…

作者头像 李华
网站建设 2026/2/22 3:18:52

异步电机软启动/软起动(调压调速) (基于导通角或者关断角控制的斜坡电压软启动,功率因数闭环软...

异步电机软启动/软起动&#xff08;调压调速&#xff09; &#xff08;基于导通角或者关断角控制的斜坡电压软启动&#xff0c;功率因数闭环软启动&#xff09;。 提供说明及资料。 异步电机软启动这事儿&#xff0c;说白了就是让电机别一上来就猛冲。直接全压启动的电流冲击能…

作者头像 李华