核心结论(一句话总结)
start(): 启动一个新线程,并让这个新线程去执行run()方法中的代码。只能调用一次。run(): 只是一个普通的方法,如果直接调用它,它会在当前调用线程中执行,不会启动新的线程。可以被多次调用。
详细解释与对比
为了让你彻底理解,我们从多个维度来对比它们。

start() 方法
当你创建一个 Thread 对象后,调用 start() 方法,会发生以下事情:
- 创建新线程:JVM(Java虚拟机)会为这个线程分配一个新的栈空间(Stack),这个栈空间是独立于主线程的。
- 线程状态变更:该线程的状态会从
NEW(新建)变为RUNNABLE(可运行)。 - 执行权交给调度器:该线程进入就绪队列,等待CPU的调度,一旦CPU分配时间片,JVM就会从这个新线程的栈中开始执行
run()方法的代码。 - 异步执行:
start()方法本身会立即返回,而不会等待run()方法执行完毕,主线程会继续向下执行,而新线程在后台并发执行,这就是所谓的“异步”。
示例代码:
class MyThread extends Thread {
@Override
public void run() {
// 这段代码将在新线程中执行
for (int i = 0; i < 5; i++) {
System.out.println("子线程正在运行: " + i);
try {
Thread.sleep(100); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class StartExample {
public static void main(String[] args) {
System.out.println("主线程开始运行...");
MyThread myThread = new MyThread();
myThread.start(); // 启动新线程
// 主线程继续执行自己的任务
for (int i = 0; i < 5; i++) {
System.out.println("主线程正在运行: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程运行结束。");
}
}
可能的输出结果(因为线程调度是随机的,所以顺序可能略有不同):
主线程开始运行...
主线程正在运行: 0
子线程正在运行: 0
主线程正在运行: 1
子线程正在运行: 1
主线程正在运行: 2
子线程正在运行: 2
主线程正在运行: 3
子线程正在运行: 3
主线程正在运行: 4
子线程正在运行: 4
主线程运行结束。
从输出可以看到,主线程和子线程是交替执行的,这证明了它们是两个独立的执行流。

run() 方法
run() 方法是 Thread 类中的一个普通方法,它实现了 Runnable 接口中的 run() 方法。
- 直接调用:如果你直接在代码中调用
thread.run(),它不会创建新线程,它只是在当前正在执行的线程(比如主线程)中,像调用任何其他普通方法一样,按顺序执行run()方法里的代码,整个过程是同步的。
示例代码:
class MyTask implements Runnable { // 使用实现接口的方式更常见
@Override
public void run() {
// 这段代码将在当前调用线程中执行
for (int i = 0; i < 5; i++) {
System.out.println("任务正在运行: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunExample {
public static void main(String[] args) {
System.out.println("主线程开始运行...");
MyTask myTask = new MyTask();
Thread myThread = new Thread(myTask);
// 直接调用 run(),没有启动新线程
myThread.run();
// 主线程会等待 run() 方法执行完毕后,才会继续执行下面的代码
System.out.println("主线程运行结束。"); // 这行代码会在 run() 执行完后才打印
}
}
可能的输出结果:
主线程开始运行...
任务正在运行: 0
任务正在运行: 1
任务正在运行: 2
任务正在运行: 3
任务正在运行: 4
主线程运行结束。
从输出可以看到,run() 方法执行完毕后,主线程才继续执行并打印 "主线程运行结束。",这证明了它是同步执行的。

对比表格
| 特性 | start() |
run() |
|---|---|---|
| 功能 | 启动一个新线程,并执行该线程的 run() 方法。 |
仅作为普通方法执行,内容写在方法体中。 |
| 线程创建 | 会创建一个新的线程。 | 不会创建新线程。 |
| 执行上下文 | 在新线程的栈空间中执行 run()。 |
在当前调用线程的栈空间中执行。 |
| 执行方式 | 异步,调用后立即返回,新线程与主线程并发执行。 | 同步,调用后会阻塞当前线程,直到 run() 方法执行完毕。 |
| 调用次数 | 一个 Thread 对象只能调用一次,多次调用会抛出 IllegalThreadStateException。 |
可以被多次调用,就像任何普通方法一样。 |
| 本质 | 是一个native方法,与JVM底层线程机制交互。 | 是一个普通的Java实例方法。 |
常见错误与陷阱
错误示例:混淆 start() 和 run()
public class CommonMistake {
public static void main(String[] args) {
System.out.println("主线程开始...");
Thread t = new Thread(() -> {
System.out.println("这是在新线程中吗?");
});
// 错误:直接调用 run,没有启动新线程
t.run();
System.out.println("主线程结束。");
}
}
输出:
主线程开始...
这是在新线程中吗?
主线程结束。
很多初学者会期望看到类似 start() 的并发输出,但实际上因为错误地调用了 run(),所有代码都在主线程中顺序执行了,失去了多线程的意义。
最佳实践
在现代Java开发中,更推荐使用 实现 Runnable 接口 或 使用 Executor 框架 来创建和管理线程,而不是直接继承 Thread 类,这样可以更好地解耦任务和线程本身。
Runnable 接口示例:
// 1. 定义任务
class MyRunnableTask implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在执行任务...");
}
}
// 2. 创建线程并启动
public class RunnableExample {
public static void main(String[] args) {
MyRunnableTask task = new MyRunnableTask();
Thread thread = new Thread(task, "我的自定义线程"); // 给线程起个名字
thread.start(); // 这里还是调用 start()
}
}
记住这个简单的类比:
start()就像是你按下了汽车的启动按钮,你按下按钮后,引擎(新线程)开始工作,而你(主线程)可以立即松开按钮去做别的事情(比如系安全带)。run()就像是你用手转动曲轴来启动引擎,你必须亲自、持续地转动,引擎才会工作,期间你不能去做别的事情。
理解 start() 和 run() 的区别是掌握Java多线程编程的第一步,也是最重要的一步。
