Java 多線程開發有兩條基礎路徑:繼承 Thread 類與實現 Runnable 介面。雙方最終都靠 Thread.start() 啟動新線程,但設計哲學、擴展性與資源共享模式截然不同。本文深度剖析兩者差異、底層原理、資源共享陷阱,並給出生產級選型建議。
class MyThread extends Thread {
private final String name;
public MyThread(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Thread: " + name + ", i=" + i);
}
}
}
// 使用
MyThread t1 = new MyThread("A");
MyThread t2 = new MyThread("B");
// ❌ 錯誤:直接呼叫 run(),在主線程順序執行
t1.run(); t2.run();
// ✅ 正確:呼叫 start(),JVM 分配新線程執行 run()
t1.start(); t2.start();
run() 只是普通方法,直接呼叫不會開新線程。start() 是 native 方法,請求 JVM 創建新線程並調度執行 run()。class MyTask implements Runnable {
private final String name;
public MyTask(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Runnable: " + name + ", i=" + i);
}
}
}
// 使用:同一 Task 實例可被多個 Thread 執行 → 實現資源共享
MyTask task = new MyTask("Shared");
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
ExecutorService.execute(Runnable) 只接受 Runnable/Callable。| 維度 | extends Thread | implements Runnable |
|---|---|---|
| 繼承限制 | 單繼承,無法繼承其他類 | 無限制,可繼承類+實現多介面 |
| 任務與線程耦合度 | 強耦合(類即線程) | 解耦(任務獨立,線程只是載體) |
| 資源共享 | 困難:每 new 一個 Thread 即新實例,需額外機制共享 | 自然:同一 Runnable 實例傳給多 Thread |
| 線程池適配 | 不直接支援 | 原生支援 |
| 訪問當前線程 | this.getName() 等 | Thread.currentThread() |
| 適用場景 | 簡單獨立線程、教學演示 | 生產環境、複雜任務、線程池、資源共享 |
class TicketThread extends Thread {
private int tickets = 10; // 每個實例獨擁 10 張
public void run() {
for (int i = 0; i < 20; i++) {
if (tickets > 0) System.out.println("售出: " + tickets--);
}
}
}
public static void main(String[] args) {
new TicketThread().start(); // 賣 10 張
new TicketThread().start(); // 另賣 10 張
new TicketThread().start(); // 又賣 10 張
// 總共賣 30 張!實際只有 10 張。
}
class TicketTask implements Runnable {
private int tickets = 10; // 同一實例,共享 tickets
public void run() {
for (int i = 0; i < 20; i++) {
if (tickets > 0) System.out.println("售出: " + tickets--);
}
}
}
public static void main(String[] args) {
TicketTask task = new TicketTask();
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
// 總共賣 10 張,邏輯正確!
// 但 tickets-- 非原子,高併發下會超賣/重賣 → 需同步
}
class SafeTicketTask implements Runnable {
private int tickets = 10;
private final Object lock = new Object();
public void run() {
while (true) {
synchronized (lock) {
if (tickets <= 0) break;
System.out.println(Thread.currentThread().getName() + " 售出: " + tickets--);
}
// 模擬耗時
try { Thread.sleep(10); } catch (InterruptedException ignored) {}
}
}
}
public class Thread implements Runnable {
private Runnable target;
public Thread(Runnable target) { this.target = target; }
@Override
public void run() {
if (target != null) target.run();
}
private native void start0(); // JNI 呼叫 OS 創建線程
}
結論:Thread 本身就是 Runnable 的實現類。Thread(Runnable target) 是組合模式,將任務委託給外部 Runnable。
Callable task = () -> {
Thread.sleep(1000);
return 42;
};
ExecutorService es = Executors.newSingleThreadExecutor();
Future future = es.submit(task);
System.out.println("結果: " + future.get()); // 阻塞獲取結果
es.shutdown();
// 避免手動 new Thread()
ExecutorService executor = Executors.newFixedThreadPool(10);
// 或自定義 ThreadPoolExecutor 精確控制核心/最大/隊列/拒絕策略
executor.execute(() -> { /* Runnable 任務 */ });
executor.submit(() -> { return "result"; }); // Callable
new Thread(task, "Order-Processor-1").start();
// 或線程池
new ThreadFactoryBuilder().setNameFormat("Order-Processor-%d").build();
// ❌ 反模式:為了覆寫 run() 而繼承 Thread
class MyThread extends Thread { ... }
// ✅ 正規:實現 Runnable,組合 Thread
class MyTask implements Runnable { ... }
new Thread(new MyTask(), "MyTask").start();
// 共享計數器
AtomicInteger count = new AtomicInteger();
// 共享 Map
ConcurrentHashMap cache = new ConcurrentHashMap<>();
// 共享隊列
BlockingQueue queue = new LinkedBlockingQueue<>();
start() 請求 OS 創建線程。run(),無線程管理能力。理解這層關係,是寫出高併發、可維護 Java 多線程程式的基石。