news 2026/4/18 5:26:22

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/8 12:31:07

YOLO模型评估指标解读:mAP、F1、IoU到底怎么看?

YOLO模型评估指标解读&#xff1a;mAP、F1、IoU到底怎么看&#xff1f; 在工业质检线上&#xff0c;一台搭载YOLOv8的视觉系统正高速扫描PCB板。屏幕上不断跳动着“缺陷”标签——但工程师却发现&#xff0c;同一块板子被反复标记出位置略有偏移的多个框&#xff0c;而某些真实…

作者头像 李华
网站建设 2026/4/16 17:04:25

YOLO模型输入分辨率选择:越高越好吗?实测告诉你答案

YOLO模型输入分辨率选择&#xff1a;越高越好吗&#xff1f;实测告诉你答案 在工业质检线上&#xff0c;一台搭载YOLOv5的视觉系统正高速运转——每秒处理30帧图像&#xff0c;检测PCB板上的微型元件。突然&#xff0c;一个仅占2像素的电阻缺失未被识别&#xff0c;导致整批产品…

作者头像 李华
网站建设 2026/4/16 13:32:09

sifu 小身高角色mod制作经验

用角色本来的骨架套小角色&#xff0c;小身高角色不动的时候会有变大问题 解决办法 解包密钥 0x40A266F41FDBCE91312FBB86060D2E9425B7D922C0CF0031F634CAD9AECB49DA blender用小孩的psk 导出fbx还是叫原来的名字 就可以解决 https://www.bilibili.com/video/BV1ixv6BhECQ

作者头像 李华
网站建设 2026/4/17 14:46:06

2025最新!10个AI论文平台测评:本科生写论文不再愁

2025最新&#xff01;10个AI论文平台测评&#xff1a;本科生写论文不再愁 2025年AI论文平台测评&#xff1a;为何值得一看&#xff1f; 随着人工智能技术的不断进步&#xff0c;越来越多的本科生在撰写论文时开始依赖AI辅助工具。然而&#xff0c;面对市场上琳琅满目的平台&…

作者头像 李华
网站建设 2026/3/25 12:04:57

从提示词撰写者到AI应用架构师——Prompt工程师的12-20K高薪进阶之路

文章介绍了Prompt工程师这一新兴职业如何从简单的提示词撰写者演变为集业务理解、技术集成与性能优化于一身的"AI应用架构师"。岗位要求具备四层技术硬实力&#xff08;Prompt工程、RAG与知识管理、模型微调、模型链与多模态&#xff09;和素质软实力&#xff08;业务…

作者头像 李华
网站建设 2026/4/16 3:38:55

大模型性能优化指南:4种简单方法提升LLM应用效果,建议收藏

本文介绍了四种提升大语言模型(LLM)应用性能的实用技巧&#xff1a;1)利用缓存token将静态内容放在提示开头&#xff0c;显著降低成本和提高速度&#xff1b;2)将用户问题置于提示末尾&#xff0c;可提升30%性能&#xff1b;3)使用提示优化器改进提示结构和内容&#xff1b;4)建…

作者头像 李华