Thread.start() 與 run() 的區別是 Java 多線程面試高頻考點,也是初學者最易踩的坑。本文通過代碼實驗、源碼分析、JVM 原理深度解析兩者本質差異,幫助徹底理解線程啟動機制。
代碼片段:
public static void main(String args[]) {
Thread t = new Thread() {
public void run() {
pong();
}
};
t.run(); // 關鍵錯誤:直接調用 run()
System.out.println("ping");
}
static void pong() {
System.out.println("pong");
}
輸出結果:pong ping (固定順序,無並發效果)
| 維度 | t.run() | t.start() |
|---|---|---|
| 線程創建 | ❌ 不創建新線程 | ✅ 創建並啟動新線程 |
| 執行線程 | 當前線程 (main) | 新線程 (Thread-0 等) |
| 調用棧 | main → run() | main → start() → JVM 本地 → run() |
| 並發性 | 無 (同步阻塞) | 有 (並行執行) |
| 輸出順序 | 確定性 (pong 後 ping) | 非確定性 (視 CPU 調度) |
| 方法性質 | 普通實例方法 | 本地方法 (native) |
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
private Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
// ...
}
run() 只是普通方法調用,若繼承 Thread 重寫 run(),直接調用即在當前棧幀執行邏輯。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0(); // 本地方法
started = true;
} finally {
if (!started) group.threadStartFailed(this);
}
}
private native void start0();
start() 是同步方法,調用本地 start0(),由 JVM 完成:分配線程棧、註冊到線程組、設置狀態、調度運行,最終在新線程中調用 run()。
使用 IDE 斷點調試,觀察線程面板:
| 誤區代碼 | 問題 | 正確寫法 |
|---|---|---|
| new Thread(() -> {}).run() | 主線程同步執行 | new Thread(() -> {}).start() |
| new MyThread().run() | 繼承 Thread 但未啟動 | new MyThread().start() |
| new Thread(new RunnableImpl()).run() | 實現 Runnable 但誤調用 | new Thread(new RunnableImpl()).start() |
| thread.run() 在循環中 | 順序執行非並發 | 線程池 ExecutorService.execute() |
run() 只是定義任務邏輯的普通方法;start() 才是啟動新線程的唯一入口,請求 JVM 創建執行實體並異步調度運行 run()。 直接調用 run() 相當於單線程順序執行,喪失多線程並發優勢。