调用 run() 方法不会启动新线程,仅同步执行逻辑;start() 才真正创建并启动新线程,之后由该线程自动调用 run()。常见错误包括直接调用 run()、测试中误用及 ide 未启用线程检查。

直接调用 run() 方法会让线程逻辑在当前线程中同步执行,根本不会启动新线程——这是多线程退化为单线程最常见、也最容易被忽略的根源。
认清 run() 和 start() 的本质区别
run() 只是一个普通方法,像任何其他实例方法一样,调用它就立即执行其中代码,不涉及线程调度;start() 才是真正请求 JVM 创建并启动一个新线程,之后由该线程自动调用其 run() 方法。
- ✅ 正确:
new Thread(task).start(); - ❌ 错误:
new Thread(task).run();或task.run(); - ⚠️ 隐蔽错误:重写了
run()却在别的地方手动调用(比如测试时为了“方便”),结果绕过了线程机制
快速定位问题的检查点
当发现并发任务明显变慢、CPU 利用率低、日志按顺序逐条打印时,优先排查以下位置:
- 所有
Thread实例或Runnable对象的使用处,搜索.run(和run(); - Lambda 表达式或匿名内部类中是否意外把
start()写成run() - 自定义线程类中,是否在构造或初始化阶段就调用了
this.run() - 单元测试里用
run()模拟执行,但忘了切换回start()上生产环境
用 IDE 和静态检查防患于未然
多数现代 IDE(如 IntelliJ IDEA)会对直接调用 Thread.run() 给出警告(例如 “Calling ‘run()’ instead of ‘start()’”)。开启相应 inspection 并设为 warning 或 error 级别。
- IntelliJ:Settings → Editor → Inspections → Java → Threading issues → “Direct call to Thread.run()”
- 可配合 Checkstyle 或 PMD 添加自定义规则,禁止在非测试源码中出现
.run()调用 - 写工具方法封装线程创建逻辑,强制只暴露
start()流程,隐藏run()调用路径
替代方案:更安全的并发编程习惯
避免裸写 Thread,转向更高层抽象,天然规避此类错误:
- 用
ExecutorService提交Runnable或Callable,如executor.submit(task) - 使用
CompletableFuture进行异步编排,执行逻辑与线程启动完全解耦 - 在 Kotlin 中用
launch { ... }(配合正确 dispatcher),Java 中用ForkJoinPool.commonPool().submit()等明确语义的操作










