java中启动多线程必须调用start()方法,绝不能直接调用run();因为start()由jvm支持,负责检查状态、注册线程并调用native方法使线程进入runnable态,而run()只是普通同步方法,直接调用不创建新线程。

Java 中 start() 方法是启动多线程的**唯一标准入口**,它的通用性体现在设计契约上:只要符合 Thread 或 Runnable 的规范,无论业务场景如何变化,都必须通过它来触发真正的并发执行。
start 方法是线程启动的统一门面
Java 不允许绕过 start() 直接并发运行逻辑——哪怕你重写了 run()、封装了复杂任务、用了 Lambda 或 Callable,最终都要靠 start()(或 ExecutorService 内部调用它)来交由 JVM 调度。这个设计屏蔽了底层 OS 线程创建细节(如 pthread_create 或 Windows CreateThread),对所有平台保持行为一致。
- 继承 Thread 类:必须调用实例的 start(),不能重复调用
- 实现 Runnable/Callable:需包装为 Thread 或提交给线程池,本质仍是触发 start()
- 使用 Executors:submit() 或 execute() 最终仍会在线程池中调用某个 Thread 的 start()
通用性不等于可重用性:一个 Thread 实例只能 start 一次
start() 的通用性建立在“单次生效”前提下。每个 Thread 对象有明确状态机(NEW → RUNNABLE → TERMINATED),start() 只在 NEW 状态下成功;再次调用会抛出 IllegalThreadStateException。这不是限制,而是保障并发模型的确定性。
- 想多次执行相同逻辑?应重新 new Thread(),或改用 Runnable + 循环 / 线程池复用
- 误写成 t.start(); t.start(); 是常见编译期无错、运行时报错的问题
- 线程池中的 Worker 线程内部也是“一次 start + 多次 run 循环”,但对外仍遵循该契约
与 run() 的分工定义了通用边界
start() 负责“启动并发上下文”,run() 负责“定义并发任务”。这种分离让 start() 成为稳定接口:它不关心 run() 里是打印日志、处理 HTTP 请求,还是训练模型——只要 run() 是 public void 且无参,start() 就能驱动它在新线程中执行。
- 直接调用 run():只是普通方法调用,在当前线程同步执行,不产生新线程
- 调用 start():JVM 触发 native 层创建 OS 线程,调度器分配时间片,真正并行
- 这种职责划分使 start() 可被框架(如 Spring Task、Quartz)统一集成,无需适配具体业务逻辑
跨版本和跨实现保持兼容
从 JDK 5 到 JDK 21,start() 的语义和异常行为从未改变。OpenJDK、Zulu、Corretto 等主流 JVM 均严格遵循 JLS 规范:检查 threadStatus、调用 start0()、加入线程组。这意味着基于 start() 编写的多线程代码,无需修改即可在不同 JDK 和容器环境(如 Docker、K8s)中可靠运行。
- 即使底层 start0() 在不同系统调用不同 API(Linux 用 clone(),Windows 用 CreateThread),上层 Java 接口完全透明
- Java 19+ 的虚拟线程(Virtual Threads)虽大幅优化资源开销,但启动方式仍是 thread.start(),保持 API 兼容
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











