start()是线程启动唯一合法入口,触发JVM创建OS线程并调度执行;run()仅为普通方法,直接调用不启新线程,仅在当前线程同步执行。

Java 线程设计把 start() 和 run() 分开,不是为了增加复杂度,而是为了明确职责、保障线程生命周期可控、避免误用引发“假并发”。这种解耦是 JVM 层面的强制约定,背后有清晰的设计逻辑和运行时约束。
start() 是线程生命周期的唯一合法入口
调用 start() 才会触发 JVM 创建操作系统级线程、分配栈空间、将线程状态从 NEW 变为 RUNNABLE,并交由调度器管理。这个过程不可绕过,也不能手动模拟。
- 重复调用
start()会抛出IllegalThreadStateException,因为线程状态只能单向流转 -
start()内部调用 native 方法start0(),这是 JVM 与操作系统协作的关键桥梁 - 主线程调用
t.start()后立即返回,不等待子线程执行完成
run() 只是普通方法,不带任何线程语义
run() 在 Java 层就是个实例方法,没有特殊权限,也不参与线程创建流程。它只负责承载任务逻辑,执行环境完全取决于谁来调它。
- 直接调
t.run():代码在当前线程(比如 main)同步执行,Thread.currentThread().getName()仍是"main" - 在
start()启动的新线程中,JVM 自动回调run(),此时currentThread()才是新线程名(如"Thread-0") - 调试或单元测试时可显式调
run(),但这属于主动降级,不是多线程行为
解耦带来真正的灵活性和复用能力
把“做什么”(run)和“谁来做、何时做”(start / 线程池 / 调度器)分开,才能支撑现代并发模型。
立即学习“Java免费学习笔记(深入)”;
- 同一个
Runnable实例可被多个Thread对象复用,也可提交给ExecutorService多次执行 - 线程池不需要每次 new Thread,只需调度已有线程去执行不同
run(),降低资源开销 - 任务逻辑无需感知线程创建细节,便于测试、替换、组合(比如包装成
FutureTask)
不遵守解耦会导致隐蔽故障
线上很多“并发没效果”“数据不一致”问题,根源就是混淆了这两个方法的语义边界。
- 误用
run()替代start():表面逻辑跑通,实则全在主线程串行阻塞,CPU 利用率低、响应延迟高 - 用
stop()或暴力中断:破坏锁状态,导致临界区数据错乱,JVM 明确废弃这些方法 - 忽略线程安全:共享
Runnable实例时若含可变状态,未加同步或使用ThreadLocal,必然出错


















