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

南通鑫旺饲料添加剂厂 2026/09/09 19:21:22

文章目录

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

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

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

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

网站建设的网络建设行业网站

JSP Session管理详解引言在JavaServer Pages(JSP)技术中,Session是服务器端用于存储特定用户会话所需属性及配置信息的类。Session可以看作是Web应用中的一种存储

2026/06/30 12:30:31

网站建设设计行业网站建设

高清还原童年记忆:DDColor人物黑白照智能着色实战在泛黄的老相册里,一张张黑白照片静静诉说着过往。父母年轻时的笑脸、儿时第一次骑自行车的瞬间、祖屋门前那棵老槐树——这些

2026/06/30 12:03:59

网站建设的网络深圳的网站建设公司

YOLOv8目标检测精度提升技巧:数据增强策略应用在智能安防摄像头误报频发、工业质检漏检率居高不下的现实挑战中,一个共性问题逐渐浮现:模型见过的“世界”太单一

2026/06/30 12:13:30

深圳外贸网站建设网站建设首页

3.5液晶显示模块3.5.1液晶原理介绍整体的LCD1602液晶显示屏采用的是点阵液晶显示的工作模式,在这个过程中,液晶显示效果通过点阵组合的形式,这样的设计

2026/06/30 12:31:31

网站建设合同宜兴网站建设

RuoYi-Vue3企业级后台管理系统终极使用指南:快速上手与完整部署教程【免费下载链接】RuoYi-Vue3🎉 (RuoYi)官方仓库 基于SpringBoot

2026/06/30 11:56:58

网站建设报价建设网站公司

第一章:Dify 1.11.1补丁升级概述Dify 1.11.1 是一次面向稳定性的补丁版本更新,主要聚焦于修复已知的安全漏洞、优化系统性能以及提升多租户环境下的资源隔离能

2026/06/30 10:32:50

中山网站建设承德网站建设

快速获取 Red Hat Enterprise Linux 7.0 镜像 ISO 的完整指南【免费下载链接】RedHatEnterpriseLinux7.0镜像ISO下载指南本仓库提供 Red Hat

2026/06/30 13:06:34

网站建设价格江苏网站建设

彻底卸载OneDrive的完整解决方案:释放Windows 10系统资源【免费下载链接】OneDrive-UninstallerBatch script to completely un

2026/06/30 12:12:59