JVM does not exit until the daemon flow is complete

I just stumbled upon the strange behavior of daemon threads, which I cannot explain. I reduced my code to a minimal, complete, and verifiable sample:

public static void main(String[] args) throws InterruptedException { Thread runner = new Thread(() -> { final int SIZE = 350_000; for (int i = 0; i < SIZE; i++) { for (int j = i + 1; j < SIZE; j++) { if (i*j == SIZE * SIZE - 1) { return; } } } }); runner.setDaemon(true); runner.start(); // Thread.sleep(1000); System.out.println("Exiting."); } 

The code executed by the runner thread takes about 12 seconds to complete in my field, and we are not interested in what it does, because I just needed to spend some time computing.

If this fragment starts as it is, it works as expected: it ends immediately after it starts. If I uncomment the Thread.sleep(1000) and run the program, it runs for about 12 seconds, then prints "Exit" and exits.

As I understand how daemon threads work, I expected this code to work for 1 second and then stop executing, since the only thread of user threads is the one that is launched using the main () method ( runner background daemon thread) , and as soon as 1000 ms is transmitted, it reaches the end of its execution, and the JVM must stop. In addition, it is rather strange that the "Exit" is printed only after 12 seconds, and not when the program starts.

Am I really wrong? How can I achieve the desired behavior (pause for a second and then stop, no matter what the flow of runners does)?

I use the 64-bit Oracle JDK 1.8.0_112 in a Linux window and it has the same behavior as when starting from the IDE or from the command line.

Thanks Andrea

+6
source share
2 answers

This is probably due to optimized counted loops that removed safepoint polls from your nested loops. Try adding the -XX:+UseCountedLoopSafepoint to the JVM startup options.

+5
source

Thread#sleep(long) pauses the main thread before it returns from its main method (i.e., before the JVM considers the program executed until there are no draws). The scheduler can then start any other runnable thread that will be a deamon thread. Be that as it may, there is no obvious reason for the JVM to force the deamon thread out before it finishes executing in order to continue in the main thread (until it has worked), so the JVM can continue its schedule. However, it can at any time choose to suspend the current thread and schedule another executable thread to run, so reproducibility is not guaranteed for your example.

You can force a preset by inserting calls to Thread#yield() or #sleep(1) in loops. I bet you will begin to see how the fragment comes out faster before it finishes the cycles.

Here you will learn more about thread states and schedules; you can find a good overview here .

Update for comment:

I cannot change the code in the background thread (this is a requirement), so I was looking for a way to stop it if it takes too much time (the description of what I am doing is stackoverflow.com/questions/41226054 / ...).

Allowed only stop the stream from , so you usually check the interrupt condition at each iteration, and if the condition is met, run the return; method return; s. An interrupt condition can be as simple as a boolean flag that is set externally (! Volatile caveat !). Thus, the simplest solution would be if the main thread set this flag after sleep.

Another possibility would be to use an ExecutorService that supports timeouts, see this Q & A for an example with ScheduledExecutorService .

However, I do not understand how the scheduler can decide to wait 12 seconds before running the System.out statement.

It does not wait 12 seconds, it allows the flow of daemons to end, because deamon matters only to the JVM when deciding whether it is safe to stop the JVM. For the scheduler, only the state of the thread matters and, as far as possible, after the main thread sleeps, it has a job (deamon) and a runnable thread (main), and there is no indication that the current thread should be suspended in favor of the thread being executed. Thread switching is also expensive to calculate, so the scheduler may be reluctant to have no indication. An indicator of switching can be sleep and productivity, but also the GC works and much more.

+2
source

Source: https://habr.com/ru/post/1013728/


All Articles