简体   繁体   English

即使主要功能退出后,该java程序如何保持运行?

[英]How this java program keeps running even after main function exits?

I am trying to learn the concurrency API of java. 我正在尝试学习Java的并发API。 Below is a sample program. 下面是一个示例程序。

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

The Runnable task being provided to the ExecutorService takes 10 seconds to complete. 提供给ExecutorService的Runnable任务需要10秒钟才能完成。 I have set a timeout of 5 seconds to get the result from the future object. 我将超时设置为5秒,以从将来的对象获取结果。 So obviously the main method is exiting after 5 seconds because of the TimeoutException being thrown. 显然,由于抛出了TimeoutException,主要方法在5秒后退出。 But the Runnable task keeps on executing even after main method exits. 但是即使主方法退出后,Runnable任务仍继续执行。

Here is the output. 这是输出。

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

Any idea ? 任何想法 ?

There are a few things going on. 发生了一些事情。 First, the threads used by Executors.newSingleThreadExecutor() are non-daemon threads. 首先, Executors.newSingleThreadExecutor()使用的线程是非守护程序线程。 As the documentation of Thread mentions, non-daemon threads will keep the JVM alive. 正如Thread的文档所述,非守护程序线程将使JVM保持活动状态。

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). Java虚拟机启动时,通常只有一个非守护线程(通常调用某些指定类的名为main的方法)。 The Java Virtual Machine continues to execute threads until either of the following occurs: Java虚拟机将继续执行线程,直到发生以下任何一种情况:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place. 已调用类Runtimeexit方法,并且安全管理器已允许进行退出操作。
  • 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. 不是守护程序线程的所有线程都已死,要么通过从调用返回到run方法,要么抛出传播到run方法之外的异常。

Second, ExecutorService.shutdown() doesn't cancel any queued or currently executing tasks. 其次, ExecutorService.shutdown()不会取消任何排队的或当前正在执行的任务。 It's merely a signal to the ExecutorService to no longer accept new tasks and to terminate once all existing tasks have completed. 这只是向ExecutorService发出的信号,它不再接受新任务并在所有现有任务完成后终止。 From the Javadoc : 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. 使用awaitTermination可以做到这一点。

If you want to try and terminate the ExecutorService immediately you must use ExecutorService.shutdownNow() . 如果要立即尝试终止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. 使用awaitTermination可以做到这一点。

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. 例如,典型的实现将通过Thread.interrupt()取消,因此任何无法响应中断的任务都可能永远不会终止。

As the Javadoc states, there is no guarantee the executing tasks will terminate even with shutdownNow . 如Javadoc所述,不能保证即使使用shutdownNow也会终止执行任务。 The developer must code the task to respond to interruptions. 开发人员必须对任务进行编码以响应中断。

This leads to the third thing: Your task doesn't respond to interruptions. 这导致第三件事:您的任务无法响应中断。 While Thread.sleep will throw an InterruptedException when the thread is interrupted you don't break out of the loop when said exception is thrown; 虽然当线程被InterruptedExceptionThread.sleep会抛出一个InterruptedException ,但是当抛出该异常时, 您不会脱离循环 your code simply prints the stack trace then continues onto the next iteration. 您的代码仅打印堆栈跟踪,然后继续进行下一个迭代。 To fix this add a break statement at the end of the catch block. 为了解决这个问题,在catch块的末尾添加一个break语句。

You also have the option of using a custom ThreadFactory via Executors.newSingleThreadExecutor(ThreadFactory) . 您还可以选择通过Executors.newSingleThreadExecutor(ThreadFactory)使用自定义ThreadFactory If you have your factory return daemon threads then the JVM will exit once main returns. 如果您拥有工厂返回守护程序线程,那么一旦主返回,JVM将退出。

shutdownNow() would try to interrupt the thread but you are catching interrupt exception in sleep. shutdownNow()会尝试中断线程,但是您正在睡眠中捕获中断异常。 So you should put the whole body of your code in try catch block. 因此,您应该将整个代码放在try catch块中。

This might helps you 这可能对您有帮助

public class Test {

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

}

The TimeoutException has occurred in the thread that is running the main() function, and not the thread being executed by executor service. TimeoutException发生在运行main()函数的线程中,而不是执行程序服务正在执行的线程中。

So although thread running the main function has completed execution, the JVM will still wait for for all other executing threads (in this case the thread that is printing numbers) to terminate. 因此,尽管运行main函数的线程已完成执行,但JVM仍将等待所有其他正在执行的线程(在本例中为打印数字的线程)终止。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM