【问题标题】:Trigger a System.exit() after Threads finishes线程完成后触发 System.exit()
【发布时间】:2018-01-30 22:58:19
【问题描述】:

我使用 main 方法从一个类中启动多个线程,

public static void main(String[] args) {
    for (int i = 0; i <= ALimit - 1; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        Thread myThread= new Thread(myThreadImplementsRunnable);
        myThread.start();
    }
}

线程完成工作后,主线程仍在运行。如果我在 for 循环之后调用 System.exit(0),我的线程将无法完成它们的执行,事实上,它们甚至都不会启动。 有没有办法在所有线程完成执行后触发 System.exit(0) 而无需在每个线程上调用 join() 方法? 感谢您的帮助。

【问题讨论】:

  • 为什么要使用 System.exit(0) 强制 JVM ... 有什么原因吗?
  • 我正在生产环境中编辑一个现有的批处理,我不希望我的 java 进程在工作完成后持有资源。

标签: java multithreading garbage-collection


【解决方案1】:

有很多方法可以做到这一点。

1.加入所有生成的线程。 (哦,对了,你不想要这个,跳到 2)

这意味着保留对所有这些线程的引用:

public static void main(String[] args) throws InterruptedException {
    Thread[] threads = new Thread[ALimit];  // array to keep track of our threads, or we could use a Collection
    for (int i = 0; i < ALimit; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        Thread myThread= new Thread(myThreadImplementsRunnable);
        threads[i] = myThread; // remember it
        myThread.start();
    }
    for (Thread thread : threads) {
        thread.join(); // wait until the thread finishes, will return immediately if it's already finished.
    }
    System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}

2。使用 CountDownLatch :

一个CountDownLatch基本上是一个倒计时,线程可以等待倒计时到0。所以如果每个线程完成倒计时,main可以等待0;

public static void main(String[] args) throws InterruptedException {
    CountDownLatch finishedRunning = new CountDownLatch(ALimit);  // Latch with ALimit countdowns needed to flip
    for (int i = 0; i < ALimit; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        Thread myThread= new Thread(() -> {
            try {
                myThreadImplementsRunnable.run();
            } finally {
                finishedRunning.countDown(); // one less to wait for, in a finally block, so exceptions don't mess up our count
            }
        };
        myThread.start();
    }
    finishedRunning.await(); // waits until the count is 0
    System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}

3.使用 ExecutorService 并关闭:

ExecutorService 为您管理线程,您可以执行任务,然后只需等待ExecutorService 终止;

public static void main(String[] args) throws InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
    for (int i = 0; i < ALimit; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        executorService.execute(myThreadImplementsRunnable);
    }
    executorService.shutdown(); // will stop accepting new tasks, but all submitted tasks so far, will still be executed
    boolean terminated = executorService.awaitTermination(3, TimeUnit.MINUTES); // we have to specify a timeout, returns a boolean which we can use to test whether it timed out or not, to maybe try and force termination
    if (!terminated) {
        // try and force things? Shut down anyway? log and wait some more?
    }
    System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}

4.使用 ExecutorService 和 futures(这真的就像再次加入所有线程,所以你可能想跳过这个):

一个ExecutorService为你管理线程,你可以提交任务,跟踪返回的Futures,然后等待每个Future的结果到达;

public static void main(String[] args) throws InterruptedException {
    Set<Future<?>> futures = new HashSet<>();
    ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
    for (int i = 0; i < ALimit; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        Future<?> future = executorService.submit(myThreadImplementsRunnable);
        futures.add(future); // remember the future, pun intended ;)
    }
    executorService.shutdown(); // make sure the services terminates its threads when they're no longer needed.
    for (Future<?> future : futures) {
        try {
            future.get();
        } catch (ExecutionException e) {
            // task failed with an exception : e.getCause() to see which
        }
    }
    System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}

对此的一种变体是将ExecutorService 包装在CompletionService 中,这将按照完成的顺序返回Futures:

public static void main(String[] args) throws InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
    CompletionService<Void> completionService = new ExecutorCompletionService<Void>(executorService);
    for (int i = 0; i <= ALimit - 1; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        completionService.submit(myThreadImplementsRunnable, null);
    }
    executorService.shutdown(); // make sure the services terminates its threads when they're no longer needed.
    for (int i = 0; i < ALimit; i++) {
        Future<?> future = completionService.take();
        try {
            future.get();
        } catch (ExecutionException e) {
            // task failed with an exception : e.getCause() to see which
        }
    }
    System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}

5.使用 CompletableFutures

Java 8 为我们带来了CompletableFutures,它允许我们使用正在运行的任务作为构建块。我们可以简单地构建一个CompletableFuture 代表我们所有的异步任务。

public static void main(String[] args) throws InterruptedException {

    CompletableFuture<?>[] completableFutures = new CompletableFuture<?>[ALimit];
    for (int i = 0; i <ALimit; i++) {
        MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
        completableFutures[i] = CompletableFuture.runAsync(myThreadImplementsRunnable);
    }
    CompletableFuture<Void> all = CompletableFuture.allOf(completableFutures);

    try {
        all.get(); // get the 'combined' result
    } catch (ExecutionException e) {
        // task failed with an exception : e.getCause() to see which
    }

    System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}

结论

CountDownLatch 可能是您想要的,它很简单,而且开销很小。

ExecutorService 是专业人士会使用的,它清楚地将线程和任务的概念分开,提供使用 Futures 的选项,您可以取消它并为单个任务提供异常处理。线程可以重用,线程的数量可以定制,与任务数量无关。但这一切可能只是矫枉过正。

CompletionService 非常适合当您需要在任务完成后立即处理它们。

CompletableFuture 提供CountDownLatch 的简单性,但线程是为您管理的。

【讨论】:

    【解决方案2】:

    使用 ExecutorService 和线程池。不要忘记关闭它。请参阅此处的示例 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

    例如

    public class MyRunnable implements Runnable{
    
        String name = "DefaultName";
    
        public MyRunnable(String name){
            this.name = name;
        }
    
        @Override
        public void run() {
            for(int i = 0; i < 10; i++){
                System.out.println(String.valueOf(i) + "# My Name: " + name);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
            ExecutorService pool = Executors.newFixedThreadPool(3);
            pool.execute(new MyRunnable("John"));
            pool.execute(new MyRunnable("Jimm"));
            pool.execute(new MyRunnable("Billy"));
            pool.shutdown();
        }
    }
    

    一旦你所有的线程都完成了,你的 main 方法就结束了,执行也完成了。

    【讨论】:

    • shutdown - 此方法不等待先前提交的任务完成执行。使用{@link #awaitTermination awaitTermination}来做到这一点。
    • 你必须仔细阅读“shutdown() 方法将允许先前提交的任务在终止之前执行,而shutdownNow() 方法阻止等待的任务启动并尝试停止当前正在执行的任务。 "
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-16
    • 2012-10-05
    • 2012-09-24
    • 2018-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多