Java并发编程——start() 和 run() 的区别(高频面试题)
这是并发基础中最经典、最容易被问到的一个点,几乎每场Java后端/并发面试都会涉及。很多人以为“调用run()就能启动线程”,其实这是个大误区。
1. 核心区别总结(背诵版)
| 维度 | start() | run() |
|---|---|---|
| 是否创建新线程 | 是(调用native方法,创建OS级线程) | 否(就是一个普通的方法调用) |
| 执行线程 | 在新线程中执行run()方法 | 在当前线程(调用者线程)中执行 |
| 调用次数 | 只能调用一次,第二次抛 IllegalThreadStateException | 可以调用多次(普通方法) |
| 并发效果 | 真正实现多线程并发执行 | 串行执行,无并发 |
| 线程状态变化 | NEW → RUNNABLE | 无状态变化(线程对象仍处于NEW或其它状态) |
| 调用栈 | 为新线程创建一个独立的调用栈 | 使用当前线程的调用栈 |
| 本质 | 启动线程的入口方法 | 线程的任务逻辑(业务代码) |
一句话总结:start() 是用来启动线程的,run() 是用来定义线程要做什么的。
直接调用 run() 只是把线程逻辑当成普通方法在当前线程里跑,完全达不到多线程的目的。
2. 代码示例对比(强烈建议自己敲一遍)
public class StartVsRunDemo {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("当前线程名称: " + Thread.currentThread().getName());
System.out.println("我是通过 start() 启动的线程");
}, "Thread-Start");
Thread t2 = new Thread(() -> {
System.out.println("当前线程名称: " + Thread.currentThread().getName());
System.out.println("我是直接调用 run() 的");
}, "Thread-Run");
// 正确方式:使用 start()
t1.start();
// 错误方式:直接调用 run()
t2.run(); // 注意这里是 run() 而不是 start()
System.out.println("main线程结束");
}
}
预期输出(顺序可能有细微差异):
当前线程名称: Thread-Start
我是通过 start() 启动的线程
当前线程名称: main ← 注意这里是 main 线程!
我是直接调用 run() 的
main线程结束
t1.start()→ 新线程执行,线程名是Thread-Startt2.run()→ 在 main线程 中执行,线程名是main
3. 底层原理简析(面试追问常考)
start()方法做了什么?
- 检查线程状态(是否已经启动过)。
- 调用本地方法
start0()(JNI → JVM底层)。 - JVM为该线程分配资源、创建OS原生线程。
- 把线程状态改为 RUNNABLE。
- 新线程启动后,JVM会自动调用该线程的
run()方法。
run()方法:- 只是
Runnable接口或Thread类中定义的一个普通实例方法。 - 如果你直接调用,它和调用
obj.someMethod()没有任何区别。
4. 常见面试追问及答案
Q1:如果你直接调用 run(),线程还会进入 RUNNABLE 状态吗?
A:不会。线程对象状态不变,run() 里的代码在当前线程(通常是 main)中同步执行。
Q2:start() 可以调用两次吗?为什么?
A:不可以。第二次调用会抛 IllegalThreadStateException,因为线程状态已经不是 NEW 了(Thread 内部有状态检查)。
Q3:run() 可以调用多次吗?
A:可以,因为它只是普通方法。
Q4:为什么 Thread 类要同时实现 Runnable,并提供 run() 方法?
A:这是模板方法模式的经典应用。start() 是固定流程,run() 是可以被子类或 Runnable 覆盖的“钩子方法”(hook)。
Q5:实际开发中有人会故意直接调用 run() 吗?
A:极少。但在某些单元测试、或想在当前线程同步执行任务逻辑时可能会用(比如测试线程内代码),但生产环境几乎不用。
5. 最佳实践
- 永远使用
start()来启动线程,不要直接调用run()。 - 推荐方式:实现
Runnable接口 +new Thread(runnable).start(),或使用线程池提交任务。 - 在现代Java中(JDK 8+),更推荐使用 ExecutorService / 线程池 或 CompletableFuture,避免手动管理线程。
掌握了 start() vs run(),你就真正理解了“线程的创建和执行是两回事”这个并发核心思想。
这是并发基础系列的补充,如果你想继续看:
- 线程生命周期(5/6种状态 + 状态转换图)
- synchronized 底层与锁升级
- volatile 原理与内存屏障
- Happens-Before 规则详解
随时说“下一题”或具体问某个点,我继续给你画图+代码+面试答案。
有疑问或想看完整可运行的Demo代码,告诉我!