Java run()方法如何工作?

Java中的multithreading是通过定义run()和调用start()来完成的。

启动委托给通过操作系统例程启动线程的本机方法,并从这个新生成的线程中调用run()。

启动独立应用程序时,会自动创建主线程以执行main()。

现在考虑这个代码 –

public class Test extends Thread { 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"); } } 

这是输出 –

 java.lang.RuntimeException: Exception from child thread at com.Test.run(Test.java:11) at java.lang.Thread.run(Thread.java:662) java.lang.RuntimeException: Exception from main thread at com.Test.main(Test.java:8) 

如果通过线程启动main()方法,为什么不运行()显示在调用层次结构的顶部?

如何在不实现Runnable的情况下生成主线程?

我没有看过JVM的内部,但我猜想JVM实例化主线程来运行main方法,但是通过直接调用本机代码来运行这个主线程,而无需通过经典的Java类和方法来开始线程。

如果通过线程启动main()方法,为什么不运行()显示在调用层次结构的顶部?

正如其他人提到的那样,这是因为“主”线程是特殊的。 它不是通过标准的Thread类机制启动,而是通过Java引导代码启动。 public static void main(String[] args)总是由本机代码的主线程运行。

另一种解释是,实际上可能存在run()方法,但是它们构建堆栈的方式是为了不使用户混淆而故意隐藏它。 例如,由于您正在执行new Thread(new Test())因此Test类实际上是Thread内部的target字段。 当后台Thread启动时,它实际上调用Thread.run() ,它具有以下代码:

 public void run() { if (target != null) { target.run(); } } 

但是我们从来没有在Thread.run()看到Thread.run()方法,尽管看起来它应该存在。 如果用户在Thread超类中覆盖它,则run()方法在stackframe中。 它可以被JDK删除以改善堆栈帧输出。

Java中的multithreading是通过定义run()和调用start()来完成的。

这是正确的,但对后人我认为重要的是要意识到你的代码有问题。 您的Test应该扩展Thread ,而应该实现Runnable 。 它的工作原理是因为Thread实现了Runnable

您应该实现Runnable并将代码更改为以下内容:

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

或者您仍然扩展Thread并将您启动线程的方式更改为以下内容。 建议使用上面的Runnable模式,因为它允许您的Test线程在必要时扩展另一个类。

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

为什么这很重要? 您当前的代码实际上是实例化2个Thread对象,但其中只有一个是start() ed并且作为后台Thread运行。 您可能会遇到以下错误:

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

main方法是由JVM在一个单独的线程中启动的,它是子线程的父线程,这就是为什么你不会在调用层次结构的顶部看到子线程。

所以在你的情况下JVM创建了一个启动你程序的线程,它也扩展了Thread。

然后在你的main方法中你创建了一个类的新实例,名为start on it,这将启动一个新线程,它是由JVM启动的线程的子线程,用于启动程序。

由于main方法是独立java程序的起点,因此JVM的责任是在单独的线程中启动它,你不要为它编写代码。

通过调用main方法JVM启动程序不需要它是一个Thread或实现Runnable,它是一个标准的过程。

内部Java虚拟机的描述

应用程序初始类的main()方法充当该应用程序初始线程的起点。 初始线程可以反过来触发其他线程。

在Java虚拟机内部,线程有两种forms:守护进程和非守护进程。 守护程序线程通常是虚拟机本身使用的线程,例如执行垃圾收集的线程。 但是,应用程序可以将它创建的任何线程标记为守护程序线程。 应用程序的初始线程 – 从main()开始的线程 – 是非守护程序线程。

只要任何非守护程序线程仍在运行,Java应用程序就会继续执行(虚拟机实例继续存在)。 当Java应用程序的所有非守护程序线程终止时,虚拟机实例将退出。 如果安全管理器允许,应用程序也可以通过调用Runtime或System类的exit()方法导致其自身死亡。

调用层次结构不受您的管理,它由底层线程调度程序控制。

例如,如果我在我的机器上运行相同的代码,那么这就是输出

 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) 

所以当你运行你的例子时,调度程序选择在main之前先放开子线程。

添加到@JB Nizet,如何调用程序,或者如何实现线程生命周期取决于底层操作系统和硬件,这些操作和硬件会有所不同。

没有单一的实施细节可以提供完整的答案,每个实施都会有所不同。

也许它是一个语义参数,但是线程和进程不是同义词。

一个进程由操作系统启动,并拥有自己的私有代码页(编译的代码集)。 线程从程序内部启动,并最初共享其父代码页(编译的代码集)。

JVM内部使用Thread来运行main方法是环境的实现细节,但不是Java语言的表示。 如果一个Thread要在main堆栈帧中报告,那么引用回源代码位置的架构将是不可能的,因为主服务线程将没有编译单元(它在JVM内部)。