如何在两个线程执行完毕后退出Java应用程序
嘿,这个需求很典型,我给你分享几种实用的实现方式,你可以根据自己的场景来选:
方法1:使用Thread.join()(简单直接,适合少量线程)
这种方式最直观——把每个线程的引用保存下来,然后在主线程里调用join()方法,主线程会阻塞等待对应的线程执行完毕,再继续往下走。
修改后的代码如下:
public static void main(String[] args) { // 先定义并保存线程对象 Thread thread1 = new Thread() { public void run() { try { employee1(); } catch (Exception e) { Logger.LogServer(e); } finally { Logger.LogServer("employee1 records inserted"); } } }; Thread thread2 = new Thread() { public void run() { try { employee2(); } catch (Exception e) { Logger.LogServer(e); } finally { Logger.LogServer("employee2 records inserted"); } } }; // 启动两个线程 thread1.start(); thread2.start(); try { // 等待thread1执行完成 thread1.join(); // 等待thread2执行完成 thread2.join(); } catch (InterruptedException e) { // 处理中断异常,记得重置线程的中断状态 Thread.currentThread().interrupt(); Logger.LogServer("主线程被中断", e); } // 到这里两个线程都已经执行完毕,应用可以安全退出了 }
注意:join()会抛出InterruptedException,一定要处理这个异常,并且重置当前线程的中断状态(Thread.currentThread().interrupt()),避免后续逻辑忽略中断信号。
方法2:使用CountDownLatch(适合线程数量较多或动态变化的场景)
如果以后你可能会增加更多线程,用CountDownLatch会更灵活。它是Java并发包提供的工具类,初始化时设置一个计数器,每个线程执行完就调用countDown()让计数器减1,主线程调用await()等待计数器归零,就代表所有线程都完成了。
代码示例:
import java.util.concurrent.CountDownLatch; public static void main(String[] args) { // 初始化计数器,值为线程数量 int threadNum = 2; CountDownLatch latch = new CountDownLatch(threadNum); new Thread() { public void run() { try { employee1(); } catch (Exception e) { Logger.LogServer(e); } finally { Logger.LogServer("employee1 records inserted"); // 线程完成,计数器减1 latch.countDown(); } } }.start(); new Thread() { public void run() { try { employee2(); } catch (Exception e) { Logger.LogServer(e); } finally { Logger.LogServer("employee2 records inserted"); // 线程完成,计数器减1 latch.countDown(); } } }.start(); try { // 等待所有线程执行完毕(计数器归零) latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Logger.LogServer("主线程被中断", e); } // 所有线程执行完成,应用退出 }
这种方式的好处是,哪怕以后线程数量变成5个,只需要修改threadNum的值,不用逐个写join(),代码扩展性更好。
方法3:使用ExecutorService(生产环境推荐,规范的线程管理方式)
如果是在生产环境中,更推荐用线程池来管理线程,ExecutorService提供了更完善的线程生命周期管理,还能复用线程。我们可以提交任务到线程池,然后等待所有任务完成后再关闭线程池。
代码示例:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public static void main(String[] args) { // 创建一个固定大小的线程池,大小为2 ExecutorService executor = Executors.newFixedThreadPool(2); // 提交第一个任务 executor.submit(() -> { try { employee1(); } catch (Exception e) { Logger.LogServer(e); } finally { Logger.LogServer("employee1 records inserted"); } }); // 提交第二个任务 executor.submit(() -> { try { employee2(); } catch (Exception e) { Logger.LogServer(e); } finally { Logger.LogServer("employee2 records inserted"); } }); // 停止接受新任务 executor.shutdown(); try { // 等待所有任务完成,最多等待1小时(可以根据业务调整超时时间) if (!executor.awaitTermination(1, TimeUnit.HOURS)) { // 超时后强制关闭线程池,中断未完成的任务 executor.shutdownNow(); Logger.LogServer("线程池超时未关闭,已强制终止"); } } catch (InterruptedException e) { // 主线程被中断,强制关闭线程池 executor.shutdownNow(); Thread.currentThread().interrupt(); Logger.LogServer("主线程被中断,线程池已强制终止", e); } // 所有任务完成,应用可以安全退出 }
这种方式不仅能等待所有线程完成,还能避免手动管理线程的麻烦,线程池还能控制并发数,防止创建过多线程导致资源耗尽,非常适合生产环境使用。
内容的提问来源于stack exchange,提问作者Sai Avinash
相关产品推荐
相关产品推荐

