Java 提供兩種創建線程的基本方式:繼承 java.lang.Thread 類與實現 java.lang.Runnable 接口。雙方均需重寫 run() 方法定義執行邏輯,但在繼承關係、資源共享、設計靈活性、線程池適配等方面存在本質差異。本文通過源碼級分析與實戰對比,給出明確選型建議。
class MyThread extends Thread {
private final String name;
private final int iterations;
private final int sleepMs;
public MyThread(String name, int iterations, int sleepMs) {
this.name = name;
this.iterations = iterations;
this.sleepMs = sleepMs;
}
@Override
public void run() {
System.out.println(name + " 開始運行,迭代 " + iterations + " 次,間隔 " + sleepMs + "ms");
for (int i = 0; i < iterations; i++) {
System.out.println(name + " -> " + i);
try { Thread.sleep(sleepMs); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
}
System.out.println(name + " 結束");
}
}
// 使用
new MyThread("Thread-A", 5, 200).start();
new MyThread("Thread-B", 3, 500).start();
this 即線程對象,調用 getName()、sleep()、interrupt() 等 Thread 方法無需轉型ExecutorService.execute() 接收 Runnable,傳入 Thread 會被當作任務在調用線程中直接運行 run(),而非新線程class MyTask implements Runnable {
private final String name;
private final int iterations;
private final int sleepMs;
public MyTask(String name, int iterations, int sleepMs) {
this.name = name;
this.iterations = iterations;
this.sleepMs = sleepMs;
}
@Override
public void run() {
// 邏輯同前
Thread current = Thread.currentThread(); // 獲取當前執行線程
System.out.println(name + " 在線程 " + current.getName() + " 中運行");
for (int i = 0; i < iterations; i++) {
System.out.println(name + " -> " + i);
try { Thread.sleep(sleepMs); } catch (InterruptedException e) { current.interrupt(); break; }
}
}
}
// 多種啟動方式
Runnable task = new MyTask("Task-1", 5, 200);
new Thread(task, "Worker-1").start(); // 直接新線程
new Thread(task, "Worker-2").start(); // 同一任務實例多線程並發運行 (共享數據需同步)
executorService.submit(task); // 線程池執行
completableFuture.runAsync(task); // 異步編排
Runnable 實例可提交給多個線程/線程池並發執行() -> { ... } 直接作為 RunnableExecutorService、CompletableFuture、ThreadFactory、ScheduledExecutorServiceRunnable| 維度 | extends Thread | implements Runnable |
|---|---|---|
| 繼承限制 | 單繼承,不可再繼承其他類 | 無限制,可實現多個接口 |
| 任務復用 | 每個任務需新建 Thread 實例 | 同一實例可多次提交/共享 |
| 線程池適配 | 差:傳入 Thread 會在調用線程同步執行 run() | 完美:標準提交類型 |
| 訪問當前線程 | 直接用 this / getName() | Thread.currentThread() |
| Lambda 支持 | 不支持 | 完美支持 (Java 8+) |
| 適用場景 | 簡單獨立任務、教學演示、需繼承 Thread 擴展功能 | 所有生產環境併發任務、線程池、異步框架 |
若任務需返回結果或拋出受檢異常,使用 Callable:
Callable callable = () -> {
Thread.sleep(1000);
return "結果數據";
};
Future future = executorService.submit(callable);
String result = future.get(); // 阻塞獲取結果
// java.lang.Thread
@Override
public void run() {
if (target != null) { // target 是 Runnable
target.run();
}
}
可見 Thread 本質上是 Runnable 的包裝器與執行載體。繼承 Thread 重寫 run() 實際上是繞過了 target,而實現 Runnable 則是標準的委託模式。
Runnable (或 Lambda):解耦任務邏輯與執行策略ExecutorService):控制並發度、複用線程、管理生命周期、異常處理new Thread():除非極簡單的一次性後台任務且生命周期極短Callable + Future/CompletableFutureScheduledExecutorService:替代 Timernew Thread(task, "Business-Worker-%d") 便於排查理解 Thread 是「執行者」,Runnable 是「任務」,將二者職責分離,是編寫可維護、可擴展、高性能 Java 併發程序的起點。