线程完成后触发System.exit()

时间:2017-08-22 15:08:52

标签: java multithreading garbage-collection

我使用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()方法? 谢谢你的帮助。

2 个答案:

答案 0 :(得分:2)

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

<强> 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.因此,如果每个完成的线程倒计时,主要可以等待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和期货(这真的像再次加入所有线程,所以你可能想跳过这个):

ExecutorService为您管理线程,您可以提交任务,跟踪返回的Future,然后只需等待每个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中,这将按照他们完成的顺序返回Future

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为我们带来了CompletableFuture,这使我们可以将运行任务用作构建块。我们可以简单地构建一个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是专业人士会使用的,它清楚地区分了线程和任务的概念,提供了使用Future的选项,您可以取消它,并为个人提供异常处理任务。线程可以重复使用,线程数量可以定制,与任务数量无关。但这一切可能只是矫枉过正。

CompletionService非常适合需要在完成任务后立即完成任务。

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

答案 1 :(得分:1)

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

E.g。

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();
    }
}

完成所有yor线程后,主方法就完成了,执行完毕。