当你创建了一个线程并重写了run()
方法,这个run()
方法并不会立即被执行。它会在你调用了线程的start()
方法之后,由Java虚拟机(JVM)的线程调度器安排在一个适当的时间点开始执行。
这里的大白话解释如下:
想象你正在经营一家繁忙的餐厅,而你雇佣了一位厨师(即你创建的线程)。这位厨师有自己的拿手菜谱(即你重写的run()
方法)。但是,厨师不会自己开始做饭,他需要等待你的指令(即调用start()
方法)。
当你告诉厨师可以开始准备食物时(调用start()
),厨师并不会立即开始烹饪。相反,他会走到厨房的入口,等待被叫到(被线程调度器选中)。在厨房里,可能还有其他厨师(其他线程)也在等待,他们都希望使用厨房的设备(CPU资源)来准备自己的菜肴。
厨房的管理员(即线程调度器)会根据一定的规则来决定哪个厨师先使用厨房。一旦轮到你的厨师,他就会走进厨房,按照自己的菜谱开始做菜(开始执行run()
方法中的代码)。厨师会一直烹饪直到他的菜做好为止(即run()
方法执行完毕),或者厨房管理员叫停(即线程被中断或终止)。
总结来说,当你创建了一个线程并重写了run()
方法,这个方法会在你调用线程的start()
方法之后,由操作系统或JVM的线程调度器安排执行。具体的执行时机取决于线程调度器的策略和当前系统中其他线程的活动情况。
在Java中,Thread
类的start()
方法并没有直接调用run()
方法的代码,这是因为start()
方法触发了线程的生命周期,导致JVM的线程调度器在适当的时候调用run()
方法。下面是start()
方法和run()
方法调用的内部机制概览:
当你调用Thread
对象的start()
方法时,以下事件序列会发生:
-
线程初始化:
start()
方法首先检查该线程是否已经启动过。如果线程尚未启动,它会调用native
方法start0()
。
-
原生方法调用:
start0()
是一个原生方法,它实际上是调用了操作系统的线程创建机制。这个方法在JVM的原生代码中实现,负责向操作系统注册一个新的线程。
-
线程调度:
- 新线程被操作系统创建并放入就绪队列中等待执行。此时,新线程的状态变为“就绪”。
-
run()
方法调用:- 当线程被操作系统调度并分配到CPU时间片时,JVM调用该线程的
run()
方法。这个调用并不是直接在start()
方法中显式发生的,而是由JVM的线程调度机制隐式处理的。
- 当线程被操作系统调度并分配到CPU时间片时,JVM调用该线程的
-
线程执行:
run()
方法开始执行,其中包含你定义的线程逻辑。当run()
方法执行完毕,线程的生命周期结束,状态变为“终止”。
由于run()
方法的调用是由JVM和操作系统协同处理的,你不会在Java代码中直接看到start()
方法调用run()
的代码。start()
方法的主要目的是初始化线程并将其交给操作系统和JVM的线程调度器,后者负责在适当的时候调用run()
方法。
请注意,直接调用Thread
对象的run()
方法并不会启动一个新线程,而是在当前线程中执行run()
方法,这与调用start()
方法的行为完全不同。正确的做法是调用start()
来启动新线程,然后由JVM调度器调用run()
方法。