You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在两个线程执行完毕后退出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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:18:45