Consider the folowing code:
public... main( String[] args ) // yeah yeah, I'm too lazy to type it out
new ThreadExample().start(); // the run function just prints out "this is run"
}
Now, it may so happen that the moment the call to start is made, the scheduler switches back to the main and the main block ends. What will happen to the thread that the start method created? There's still a non-daemon thread running (the one we created) meaning the program should continue execution or it can become a zombie thread (I still don't fully understand what that entails...). What might (or what passes for "might" in the absence of synchronization) happen?
Edit:
This question is better rephrased as: "What exactly does the start() method call do?"
More may be found at: Why do we call Thread.start() method?
CodePudding user response:
TL;DR
In your example, the thread will be started and the JVM won't shutdown until that thread also terminates (unless a different shutdown event is triggered). You don't need extra synchronization for that.
longer description
You are starting the thread in your main method.
Because the start()
method finishes before the main
method stops, the application will not shut down. As mentioned in the comments of your question (and also directly in your question), the JVM doesn't attempt to shut down before all non-daemon threads are stopped.
When you create a thread, it will be in ThreadState.NEW
but as soon as you have called start()
, it will be in ThreadState.RUNNABLE
(or similar if it had already performed some work) and will stop the application from shutting down.
Thread t = new Thread(()->{
//you can do something here if you want to
});
t.start();
System.out.println(t.getState());//this will never be NEW
If a thread is neither NEW
nor TERMINATED
, it is alive hence it it will prevent the JVM from shutting down.
So, your second thread would continue running until it finishes or something else stops the JVM.
As soon as you call Thread#start
, you have a thread preventing the JVM from shutting down, no matter whether the thread actually started working.
However, as mentioned in the comments, this can be different if Thread#start
is invoked after the last non-daemon thread has stopped. If the JVM is already shutting down, calling Thread#start
on another thread won't cancel shutdown.
If the last non-daemon thread stops and a daemon thread attempts to start a non-daemon thread "at the same time", you will have a race condition and the thread might win or be stopped.
If the thread is actually marked as started before the last (non-daemon thread) thread stops, it will run normally and the JVM won't shutdown.
However, if the JVM detects all threads are stopped before the new thread is marked as started/RUNNABLE
, the JVM would initiate the shutdown sequence and kill the newly started thread after all shutdown hooks finish.
If you are using virtual threads, it should be noted that these always are daemon threads.
CodePudding user response:
FYI: You may have heard of "zombies" from Linux. Linux, processes refer to other processes by their system-wide PIDs (Process IDs). The OS re-uses PIDs, but it will never re-use PID C* until some live process P* has received notification that C has died and, that its PID no longer is valid. That happens when P calls wait()
or one of several other, similar system calls.
A zombie process in Linux simply is the PID of some dead process for which no other living process has yet received the death notice. If process P itself dies without ever waiting for its zombie child C, then the init
process will wait
for both of them, and the zombie will be reaped.
The only way that zombies can be a problem is if process P runs for a long time and continually creates new children without ever wait
ing for any of them. PIDs are a limited resource, and eventually, the process table could be so clogged with zombies that fork()
calls start failing with ENOMEM
.
I'm not aware of anything similar happening with Java threads. The Java Thread
object representing a dead thread gets managed (garbage collected) just like any other Java object, and if the OS requires some kind of cleanup after the underlying native thread has ended, AFAIK, the JVM is supposed to automatically take care of that for you.
* I'm using "P" for "parent" and "C" for "child" because that's the most common way for one process to know the PID of another.