兰州网站建设中山网站建设

福州欧派兰商贸有限公司 2026/09/09 17:45:25

文章目录

  • 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!

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

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

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

天津网站建设长沙营销型网站建设

部署搭建流程分享环境准备:确保服务器已经安装了所需的开发环境和依赖。常见的开发环境有Python、Node.js等,常见的依赖有数据库和缓存等。下载源码:从源

2026/06/30 12:17:31

南通网站建设益阳网站建设

一、HLS编译IR中间表示(Intermediate Representation, IR)是编译器在将源代码转换为目标代码过程中使用的一种中间形式的程序表示;hls co

2026/06/30 13:30:06

网站建设设计十堰网站建设

快速体验打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容:创建一个最简MyBatis-Spring Boot示例项目,要

2026/06/30 10:42:51

山西网站建设济南网站建设公司

跌落测试介绍跌落测试是模拟产品在运输、搬运、仓储过程中可能发生的跌落、碰撞情况,评估产品及包装抗冲击能力、结构完整性和防护性能的一项可靠性测试,广泛应用于包装运输、电子电器

2026/06/30 12:15:00

网站建设中南京网站建设公司

第一章:Open-AutoGLM智能体构建技术概述Open-AutoGLM 是一种基于生成语言模型(GLM)的智能体架构,旨在实现自主任务分解、

2026/06/30 11:26:25

个人网站建设深圳 网站建设

快速体验打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容:创建一个Python代码自动修复工具,能够检测并修复TABER

2026/06/30 13:28:36

黄石网站建设汕头网站建设

全面掌握Netperf:网络性能测试的瑞士军刀从零开始,深度解析Netperf的安装、使用和实战应用目录全面掌握Netperf:网络性能测试的瑞士军刀

2026/06/30 11:46:57

上海网站建设招商网站建设

第一章:Excel集成Dify后内存飙升?初探现象与根源近期多位用户反馈,在将 Excel 与 Dify 平台进行数据对接后,系统内存占用急剧上

2026/06/30 12:44:32