Multi-threading in Java is done by defining run() and invoking start().
Start delegates to a native method that launches a thread through operating system routines and
main method is started in a separate thread by JVM, it is the parent thread of child thread, thats why you dont see child thread at the top of invocation hierarchy.
So in your case JVM created a thread started your program, which also extends Thread.
Then in your main method you created a new instance of your class, called start on it, this will start a new thread which is child of thread started by JVM to launch your program.
Since main method is a starting point for a standalone java program, it is the responsiblity of JVM to launch it in a separate thread, you dont write code for it.
While launching a program by calling main method JVM doesn't need it to be a Thread or implement Runnable, it is a standard procedure.
Description from Inside Java Virtual Machine
The main() method of an application's initial class serves as the starting point for that application's initial thread. The initial thread can in turn fire off other threads.
Inside the Java virtual machine, threads come in two flavors: daemon and non- daemon. A daemon thread is ordinarily a thread used by the virtual machine itself, such as a thread that performs garbage collection. The application, however, can mark any threads it creates as daemon threads. The initial thread of an application--the one that begins at main()--is a non- daemon thread.
A Java application continues to execute (the virtual machine instance continues to live) as long as any non-daemon threads are still running. When all non-daemon threads of a Java application terminate, the virtual machine instance will exit. If permitted by the security manager, the application can also cause its own demise by invoking the exit() method of class Runtime or System.
The call hierarchy is not governed by you it is governed by the underlying thread scheduler.
So for example If I run the same code on my machine this is the output
Exception in thread "main" java.lang.RuntimeException: Exception from main thread
at TestThread.main(TestThread.java:6)
Exception in thread "Thread-1" java.lang.RuntimeException: Exception from child thread
at TestThread.run(TestThread.java:9)
at java.lang.Thread.run(Thread.java:662)
so when you ran your example, scheduler choose to let go child thread first before main.
Adding to @JB Nizet, how a program will be invoked, or how a thread lifecycle will be implemented depends from underlying OS and hardware, which will and does vary.
No single implementation details would provide a complete answer, each implementation will vary.
If main() method is launched via a thread why doesn't run() show up at the top of invocation hierarchy?
As others have mentioned, this is because the "main" thread is special. It is not launched through the standard Thread
class mechanisms but instead through Java bootstrap code. The public static void main(String[] args)
is always run by the main thread from native code.
Another explanation is that there actually maybe a run()
method but the way they build the stackframe is hiding it on purpose for the sake of not confusing the user. For example, since you are doing a new Thread(new Test())
then your Test
class is actually the target
field inside of the Thread
. When the background Thread
is started then it actually calls Thread.run()
which has the code:
public void run() {
if (target != null) {
target.run();
}
}
But we never see the Thread.run()
method in the stackframe although it seems like it should be there. The run()
method would be in the stackframe if the user overrode it in a Thread
superclass. It could be removed by the JDK to improve stackframe output.
Multi-threading in Java is done by defining run() and invoking start().
This is correct but for posterity I thought it was important to realize that your code has a problem. Your Test
class should not be extending Thread
but instead should be implementing Runnable
. It works because Thread
implements Runnable
.
Either you should implement Runnable
and change your code to something like this:
public class Test implements Runnable {
public static void main(String[] args) throws Exception {
new Thread(new Test()).start();
throw new RuntimeException("Exception from main thread");
}
public void run() {
throw new RuntimeException("Exception from child thread");
}
}
Or you still extend Thread
and change the way you start your thread to something like the following. The above Runnable
pattern is recommended since it allows your Test
thread to extend another class if necessary.
public class Test extends Thread {
public static void main(String[] args) throws Exception {
new Test().start();
throw new RuntimeException("Exception from main thread");
}
@Override
public void run() {
throw new RuntimeException("Exception from child thread");
}
}
Why is this important? You current code is actually instantiates 2 Thread
objects but only one of them is start()
ed and is running as a background Thread
. You could have something like the following bug:
public class Test extends Thread {
public static void main(String[] args) throws Exception {
Test test = new Test();
new Thread(test).start();
// this is not interrupting the background thread
test.interrupt();
I haven't looked at the internals of the JVM, but I would guess that the JVM instantiates the main thread to run the main method, but runs this main thread by invoking native code directly, without going through the classical Java classes and methods to start the thread.
Maybe it is a semantic argument, but a Thread and a Process are not synonymous.
A process is launched by the operating system, and has it's own private code pages (compiled set of code). A Thread is launched from the program internals, and initially shares its parent's code pages (compiled set of code).
That the JVM internally uses a Thread to run the main method is an implementation detail of the environment, but not a representation of the Java language. If a Thread was to report in the main
stack frames, the architecture of referring back to the source code position would be impossible, because that main-serving thread would have no compilation unit (it is internal to the JVM).