java - 即使在 main 函数退出后,这个 java 程序如何继续运行?

标签 java concurrency

我正在尝试学习 java 的并发 API。下面是一个示例程序。

    class WaitTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        ExecutorService executorService = null;
        try {
            executorService = Executors.newSingleThreadExecutor();
            Future<?> future = executorService.submit(() ->
                {
                    for (int i = 0; i < 100; i++) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Printing " + i);
                    }
                });
            future.get(5, TimeUnit.SECONDS);
            System.out.println("Reached successfully");
        } finally {
            if (executorService != null) {
                executorService.shutdown();
            }
        }
    }
}

提供给 ExecutorService 的 Runnable 任务需要 10 秒才能完成。我已将超时设置为 5 秒以从 future 对象获取结果。很明显,由于抛出 TimeoutException,main 方法在 5 秒后退出。但即使在 main 方法退出后,Runnable 任务仍继续执行。

这是输出。

Printing 0
Printing 1
Printing 2
Printing 3
Printing 4
Printing 5
Printing 6
Printing 7
Printing 8
Printing 9
Printing 10
Printing 11
Printing 12
Printing 13
Printing 14
Printing 15
Printing 16
Printing 17
Printing 18
Printing 19
Printing 20
Printing 21
Printing 22
Printing 23
Printing 24
Printing 25
Printing 26
Printing 27
Printing 28
Printing 29
Printing 30
Printing 31
Printing 32
Printing 33
Printing 34
Printing 35
Printing 36
Printing 37
Printing 38
Printing 39
Printing 40
Printing 41
Printing 42
Printing 43
Exception in thread "main" java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask.get(FutureTask.java:205)
    at ocp.WaitTest.main(ConcurrencyTest.java:89)
Printing 44
Printing 45
Printing 46
Printing 47
Printing 48
Printing 49
Printing 50
Printing 51
Printing 52
Printing 53
Printing 54
Printing 55
Printing 56
Printing 57
Printing 58
Printing 59
Printing 60
Printing 61
Printing 62
Printing 63
Printing 64
Printing 65
Printing 66
Printing 67
Printing 68
Printing 69
Printing 70
Printing 71
Printing 72
Printing 73
Printing 74
Printing 75
Printing 76
Printing 77
Printing 78
Printing 79
Printing 80
Printing 81
Printing 82
Printing 83
Printing 84
Printing 85
Printing 86
Printing 87
Printing 88
Printing 89
Printing 90
Printing 91
Printing 92
Printing 93
Printing 94
Printing 95
Printing 96
Printing 97
Printing 98
Printing 99

有什么想法吗?

最佳答案

有一些事情正在发生。首先,Executors.newSingleThreadExecutor() 使用的线程是non-daemon线程。正如 Thread 的文档中提到的,非守护线程将使 JVM 保持 Activity 状态。

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
  • All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.

其次,ExecutorService.shutdown() 不会取消任何排队或当前正在执行的任务。它只是向 ExecutorService 发出不再接受新任务并在所有现有任务完成后终止的信号。来自 the Javadoc :

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.

This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.

如果您想立即尝试终止 ExecutorService,您必须使用 ExecutorService.shutdownNow() .

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

This method does not wait for actively executing tasks to terminate. Use awaitTermination to do that.

There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.

正如 Javadoc 所述,即使使用 shutdownNow,也不能保证正在执行的任务会终止。开发人员必须对任务进行编码以响应中断。

这导致了第三件事:您的任务不会响应中断。当线程被中断时 Thread.sleep 将抛出一个 InterruptedException 当抛出所述异常时,您不会跳出循环;您的代码只是打印堆栈跟踪,然后继续下一次迭代。要解决此问题,请在 catch block 的末尾添加一个 break 语句。

您还可以选择通过 Executors.newSingleThreadExecutor(ThreadFactory) 使用自定义 ThreadFactory。如果您让工厂返回 daemon 线程,那么 JVM 将在 main 返回后退出。

关于java - 即使在 main 函数退出后,这个 java 程序如何继续运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53778152/

相关文章:

ios - 同步排队异步操作

ios - dispatch_sync(dispatch_get_global_queue(xxx), task) 是同步还是异步

java - 匿名类中的 Super (Java)

java - 关于OpenLRW部署到mongo docker实例时出现异常

java - ListSelectionListener 中的 valueChanged 不起作用

带有未转义引号的 Java CSV 解析器

java - 如何提高这段代码的io性能

java - 二叉搜索树 (BST) 中的多线程插入

子信号和父信号之间的并发竞争

java - 使用java和mysql服务器的读/写模式