【面试题】深度解析Java多线程中的 run() 与 start() 有什么区别?
下面这个是我2022年6月写的文章,当时刚刚学多线程,所以对这个问题的理解很片面~~~
下文是现在2022.11.30,我对其新的见解!站在JVM底层源码的角度来解析这个问题!!!
start() 方法是启动线程,并由 JVM 来调用线程的 run() 方法执行,是真正的多线程!
这时此线程是处于就绪状态, 并没有运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。
run() 方法里面封装的是需要被线程执行的逻辑代码,如果直接调用run()方法和调用普通方法没有区别(单线程)!
run() 可以重复调用,而 start()只能调用一次
如果直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
调用 start 方法方可启动线程并使线程进入就绪状态;而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。
我们可以用一段简单的代码,通过调试来证明!
- public class Demo {
- public static void main(String[] args) {
- Thread t = new Thread(() -> {
- System.out.println("dsfdsfds");
- });
- t.start();
- }
- }
start()方法上面打一个断点
run()方法上面打一个断点
通过调试发现,确实是会跳到run()里面! 但是具体怎么进去的?在IDEA中是看不出来的!因为这里是由JVM(C++写的)编写的,如果我们想要搞清楚它们是要去阅读JVM源码的!
(1)使用 new Thread() 创建一个线程,然后调用 start0()方法 进行 Java 层面的线程启动
当我们自己写的代码使用 start() 方法的时候,表示启动这个线程,在该方法的内部会去调start0() 这个本地方法!
(2)调用本地方法start0(),去调用 JVM 中的JVM_StartThread方法进行线程创建和启动
- public
- class Thread implements Runnable {
- private static native void registerNatives();
- static {
- registerNatives();
- }
- ...
- }
start0() 会去调 JVM_StartThread 进行线程创建、启动,那它们是如何扯上关系的???
在 Java 中的 Thread 类,当我们初始化的时候(new的时候)会先执行静态代码块里的方法!
也就是说,会先调一下 registerNatives() 本地方法,如下图:
(3)调用 new JavaThread(&thread entry, sz) 进行线程的创建,并根据不同的操作系统平台调用对应的 os::create thread 方法进行线程创建
此时操作系统会切换为内核态!!!
(4)新创建的线程状态为 lnitialized(初始化状态),调用了sync->wait() 的方法进行等待(先阻塞!!!),等到被唤醒才继续执行 thread->run()
等待JVM将其唤醒
(5)调用 Thread::start(native thread) 方法进行线程启动,此时将线程状态设置为RUNNABLE,接着调用 os::start_thread(thread),根据不同的操作系统选择不同的线程启动方式
(6)线程启动之后状态设置为RUNNABLE,并唤醒第4步中等待的线程,接着执行 thread->run() 的方法
(7) JavaThread::run()方法会回调第1步new Thread中复写的run()方法。
纵观整个流程,Java Thread --> JVM JavaThread --> OS Thread,在JVM操作OS创建线程时,会涉及到用户态到内核态的切换!!!这也是Java线程是一个很“重”的操作的原因!!!
同样这也是Java线程池为什么优于多线程的原因!为什么要有线程复用机制的原因!!!
具体看这里 ↓ ↓ ↓
【并发】深入理解Java线程的底层原理_面向鸿蒙编程的博客-CSDN博客https://blog.csdn.net/weixin_43715214/article/details/128099128