核心区别一句话总结
start(): 启动一个新线程,并让这个新线程去执行run()方法中的代码。当前线程会继续执行,不会等待新线程结束。run(): 只是一个普通的方法,如果直接调用它,它会在当前线程中执行,并不会创建新的线程。
详细解释
start() 方法
当你调用一个线程对象的 start() 方法时,会发生以下事情:

- 创建新线程:JVM 会为这个线程对象创建一个新的执行栈(调用栈)。
- 调用
run():新线程会自动调用run()方法,并将run()方法中的代码作为它的任务来执行。 - 并发执行:
start()方法会立即返回,而新线程会与调用start()的线程(通常是主线程)并发(并行或并发)地执行,主线程不会阻塞,会继续执行它自己的代码。
关键点:start() 是“启动”一个线程的入口,它告诉 JVM:“请为我创建一个新的线程,并让它去执行 run() 方法里的任务”。
run() 方法
run() 方法是线程要执行的任务体,它本身就是一个普通的 Java 实例方法。
- 直接调用:如果你直接
thread.run(),run()方法里的代码会在当前正在执行的线程(比如主线程)中按顺序执行,这没有创建任何新线程,代码仍然是单线程执行的。
代码示例
通过下面的例子,你可以非常直观地看到两者的区别。
示例 1:正确使用 start()(多线程)
class MyThread extends Thread {
@Override
public void run() {
// 这是线程要执行的任务
for (int i = 1; i <= 5; i++) {
System.out.println("子线程 " + Thread.currentThread().getName() + " 正在运行,i = " + i);
try {
// 模拟耗时操作
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class StartExample {
public static void main(String[] args) {
System.out.println("主线程 " + Thread.currentThread().getName() + " 开始运行。");
// 创建一个线程对象
MyThread thread = new MyThread();
// 调用 start() 方法启动线程
thread.start();
// 主线程继续执行自己的任务
for (int i = 1; i <= 5; i++) {
System.out.println("主线程 " + Thread.currentThread().getName() + " 正在运行,i = " + i);
try {
Thread.sleep(300); // 主线程休眠时间不同,更容易观察交错执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程 " + Thread.currentThread().getName() + " 运行结束。");
}
}
可能的输出结果(顺序可能因线程调度而异):

主线程 main 开始运行。
子线程 Thread-0 正在运行,i = 1
主线程 main 正在运行,i = 1
子线程 Thread-0 正在运行,i = 2
主线程 main 正在运行,i = 2
子线程 Thread-0 正在运行,i = 3
主线程 main 正在运行,i = 3
子线程 Thread-0 正在运行,i = 4
主线程 main 正在运行,i = 4
子线程 Thread-0 正在运行,i = 5
主线程 main 正在运行,i = 5
主线程 main 运行结束。
子线程 Thread-0 正在运行,i = 5 // 子线程可能稍后才结束
分析:
- 主线程开始,然后调用了
thread.start()。 start()方法返回后,主线程和子线程Thread-0同时开始运行,它们各自的for循环被并发执行,输出结果交错出现,证明了多线程的特性。
示例 2:错误使用 run()(单线程)
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("子线程 " + Thread.currentThread().getName() + " 正在运行,i = " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunExample {
public static void main(String[] args) {
System.out.println("主线程 " + Thread.currentThread().getName() + " 开始运行。");
MyThread thread = new MyThread();
// 注意:这里直接调用了 run() 方法,而不是 start()
thread.run();
// 这部分代码会在线程 run() 方法执行完毕后才会执行
for (int i = 1; i <= 5; i++) {
System.out.println("主线程 " + Thread.currentThread().getName() + " 正在运行,i = " + i);
}
System.out.println("主线程 " + Thread.currentThread().getName() + " 运行结束。");
}
}
输出结果:
主线程 main 开始运行。
子线程 main 正在运行,i = 1
子线程 main 正在运行,i = 2
子线程 main 正在运行,i = 3
子线程 main 正在运行, i = 4
子线程 main 正在运行, i = 5
主线程 main 正在运行,i = 1
主线程 main 正在运行,i = 2
主线程 main 正在运行,i = 3
主线程 main 正在运行,i = 4
主线程 main 正在运行,i = 5
主线程 main 运行结束。
分析:
- 主线程调用了
thread.run()。 run()方法作为一个普通方法,在主线程中顺序执行,所有的 "子线程" 输出都来自主线程(Thread.currentThread().getName()打印出main)。- 只有当
run()方法执行完毕后,主线程才会继续执行它自己的for循环。 - 整个过程是单线程的,没有并发执行。
总结对比
| 特性 | start() |
run() |
|---|---|---|
| 功能 | 启动一个新线程,并使其进入就绪状态。 | 定义线程要执行的任务体。 |
| 执行者 | 新创建的线程。 | 调用该方法的当前线程。 |
| 是否创建新线程 | 是。 | 否。 |
| 调用方式 | thread.start() |
thread.run() |
| 程序行为 | 多线程并发执行。 | 单线程顺序执行。 |
| 类比 | 按下遥控器的“开机”按钮,电视开始播放节目。 | 你自己手动去操作电视的按钮,改变频道或音量。 |
最佳实践
在 Java 中,除了直接继承 Thread 类,更推荐使用 Runnable 接口 或 Callable 接口(配合 Future 和 ExecutorService)来创建线程,这样可以更好地解耦任务代码和线程管理代码。

使用 Runnable 的例子:
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("任务正在由 " + Thread.currentThread().getName() + " 执行。");
}
}
public class RunnableExample {
public static void main(String[] args) {
// 1. 创建任务
MyTask task = new MyTask();
// 2. 创建线程,并将任务交给线程
Thread thread = new Thread(task);
// 3. 启动线程(关键!)
thread.start();
System.out.println("主线程 " + Thread.currentThread().getName() + " 继续执行。");
}
}
这个例子中,Runnable 对象 task 包含了任务逻辑,而 Thread 对象 thread 负责执行这个任务,调用 thread.start() 同样会创建新线程去执行 task.run()。
记住这个黄金法则:要启动新线程,永远调用 start(),而不是 run()。
