public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
pong();
}
};
t.run(); // 關鍵:這裡呼叫的是 run() 而非 start()
System.out.println("ping");
}
static void pong() {
System.out.println("pong");
}
輸出結果:
pong
ping
永遠是固定順序,不會 出現 ping pong 或交替輸出。
| 維度 | t.start() | t.run() |
|---|---|---|
| 是否建立新執行緒 | 是,JVM 分配新執行緒、棧幀、程式計數器 | 否,單純在當前執行緒(main)中同步呼叫方法 |
| 執行模式 | 並發:新執行緒與 main 執行緒同時運行,搶佔 CPU | 串行:run() 結束前,main 執行緒卡住等待 |
| run() 執行時機 | 由執行緒調度器決定,非即時 | 立即、同步執行 |
| 可重複呼叫 | 否:第二次呼叫拋 IllegalThreadStateException | 是:只是普通方法,可重複呼叫 |
| 適用場景 | 所有需並發執行的場景 | 單元測試驗證 run() 邏輯、簡單同步任務 |
// Thread.java 簡化版
public synchronized void start() {
if (threadStatus != 0) throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0(); // native 方法,向 OS 請求建立執行緒
started = true;
} finally {
if (!started) group.threadStartFailed(this);
}
}
private native void start0(); // JNI 呼叫 OS API (pthread_create / CreateThread)
run() 方法。run()。@FunctionalInterface
public interface Runnable {
void run(); // 無任何特殊語義
}
public void run() {
if (target != null) target.run(); // 若用 Runnable 建構,委派給 target
}
直接呼叫 t.run() 等同於 t.target.run(),完全在呼叫者執行緒棧幀中執行,零並發。
若改為 t.start(),兩執行緒並發,輸出順序取決於 CPU 調度:
ping。pong。pong() 內有長運算或 Thread.yield())。但題目中 System.out.println 有同步鎖,且指令極少,通常 main 執行緒搶先完成,輸出 pong ping(新執行緒啟動需幾百微秒,main 已印完)。
誤區:「繼承 Thread 並重寫 run(),然後呼叫 run() 就能多執行緒。」
正確:必須呼叫 start()。若偏好實作 Runnable,用 new Thread(runnable).start()。
// 1. 獨立任務類(可復用、可測試)
class PingTask implements Runnable {
@Override public void run() { System.out.println("pong"); }
}
// 2. 啟動
new Thread(new PingTask()).start();
// 或 Lambda
new Thread(() -> System.out.println("pong")).start();
Runnable 可提交給 ExecutorService、CompletableFuture、ScheduledExecutorService 等高階 API。run() 內部與 start() 後第一行。start():會出現 Thread-0、Thread-1 等新執行緒。run():只有 main 一條執行緒。start() = 啟動新執行緒(非同步、並發、由 OS 調度)。run() = 任務邏輯入口(同步、串行、普通方法)。start()(或線程池)建立,絕不可直接呼叫 run() 期望並發。