简体   繁体   中英

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

I am trying to learn the concurrency API of java. 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. I have set a timeout of 5 seconds to get the result from the future object. So obviously the main method is exiting after 5 seconds because of the TimeoutException being thrown. But the Runnable task keeps on executing even after main method exits.

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. As the documentation of Thread mentions, non-daemon threads will keep the JVM alive.

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.

Second, ExecutorService.shutdown() doesn't cancel any queued or currently executing tasks. It's merely a signal to the ExecutorService to no longer accept new tasks and to terminate once all existing tasks have completed. From 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.

If you want to try and terminate the ExecutorService immediately you must use 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.

As the Javadoc states, there is no guarantee the executing tasks will terminate even with 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; 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.

You also have the option of using a custom ThreadFactory via Executors.newSingleThreadExecutor(ThreadFactory) . If you have your factory return daemon threads then the JVM will exit once main returns.

shutdownNow() would try to interrupt the thread but you are catching interrupt exception in sleep. So you should put the whole body of your code in try catch block.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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