start() 是 JVM 启动线程的唯一入口,触发操作系统级线程创建与状态迁移;run() 仅为普通 Java 方法,直接调用不产生新线程,仅在当前线程执行。

start() 是 JVM 启动线程的唯一契约入口
start() 方法被声明为 synchronized,内部调用私有 native 方法 start0()。这个 native 方法由 JVM 实现,最终通过操作系统 API(如 Linux 的 pthread_create)申请内核级线程资源。只有走通这条路径,JVM 才会为该 Thread 对象分配独立栈空间、注册调度单元、将线程状态从 NEW 变更为 RUNNABLE,并将其加入操作系统的就绪队列。
run() 本质就是一个普通 Java 方法
run() 在 Thread 类中是纯 Java 实现的实例方法,定义如下:
@Override
public void run() {
if (target != null) {
target.run();
}
}
它不涉及任何线程生命周期管理逻辑。直接调用时,JVM 完全不感知“线程启动”这件事——不会创建新栈帧、不切换执行上下文、不更新线程状态、不通知调度器。整个过程就像调用 obj.doSomething() 一样,只是在当前线程的调用栈上压入一个新帧。
关键差异体现在运行时行为上
- 调用
t.start()后,Thread.currentThread().getName()在 run 体内输出的是Thread-0等新线程名;而t.run()中永远输出main -
t.start()返回后主线程立即继续执行,子线程在后台异步运行;t.run()则会阻塞主线程,直到方法体全部执行完 - 重复调用
t.start()抛IllegalThreadStateException;t.run()可调用任意次,无状态约束 - 调试时,只有
start()成功后,IDE 的 Debug 视图才能看到独立的线程条目和专属调用栈
JVM 不允许绕过 start() 模拟线程创建
因为线程模型是 JVM 与操作系统协同构建的底层契约:start() 触发的是 OS 级资源分配和状态机迁移;run() 只是用户代码逻辑的容器。任何试图在 Java 层“手动触发 run 来模拟并发”的做法,都跳过了线程调度、内存可见性保障、中断响应机制等 JVM 必须介入的关键环节。多核并行、线程安全语义、volatile 和 synchronized 的底层支持,全都依赖于 start() 所启动的真实线程实体。

















