start()方法执行完不等于线程立即运行,它仅将线程置为就绪状态,实际执行需等待操作系统调度分配cpu时间片。

start() 方法执行完 ≠ 线程立刻运行
调用 start() 方法只是向 JVM 发出一个“请启动这个线程”的请求,JVM 会为它分配资源、初始化线程栈,并将该线程置为 就绪(Runnable)状态。但此时线程还没拿到 CPU 时间片,操作系统调度器还没轮到它——就像你按了电梯上行键,电梯灯亮了(就绪),但电梯未必马上来。
几个可验证的实证方式
-
观察线程状态变化:在 start() 后立即调用
getState(),大概率返回RUNNABLE(注意:这是就绪态,不是正在运行)。若真在运行中,状态也叫 RUNNABLE,但仅靠这个方法无法区分“就绪”和“运行中”,需配合更底层手段。 - 插入日志 + 时间戳:在线程 run() 方法第一行打印当前时间,在 start() 调用后也立刻打印时间。多次运行会发现两者间隔不为零,有时甚至达毫秒级——说明中间存在调度延迟。
- 强制制造竞争环境:启动大量线程(比如 100 个),每个只做简单操作(如 ++ 一个共享计数器),再检查最终结果是否符合预期顺序。你会发现执行顺序杂乱、输出交错,且部分线程明显“迟到”,这直接反映它们并未在 start() 返回瞬间获得 CPU。
-
借助 JVM 工具观测:用
jstack <pid></pid>抓取线程快照,常能看到多个线程处于java.lang.Thread.State: RUNNABLE,但其中不少实际在等待调度(即“就绪排队中”),而非真正占用 CPU 执行字节码。
为什么不能立即运行?关键在调度机制
操作系统对线程的调度受多种因素影响:
- CPU 核心数量有限,而就绪线程可能远多于核心数;
- 当前正在运行的线程可能尚未被抢占(尤其在非抢占式或低负载场景下);
- 线程优先级、亲和性设置、内核调度策略(如 CFS)都会干预执行时机;
- JVM 自身也可能插入 safepoint 或 GC 暂停,进一步推迟调度。
对比 run() 直接调用就一目了然
如果绕过 start(),直接调用 thread.run(),代码会在当前线程同步执行,毫秒级响应、无状态切换、可预测顺序——这恰恰反衬出 start() 的异步性和调度依赖性。它本质是发起一个并发请求,不是执行一条语句。











