繼承 java.lang.Thread 是 Java 創建線程的另一種方式。雖然工程實踐推薦 Runnable,但理解 Thread 繼承機制、構造器鏈、線程組、堆棧大小等底層細節,對於閱讀源碼、排查線程異常、設計線程池至關重要。本文深度解析 8 個構造器、線程命名規則、start() 與 run() 語義差異、守護線程、線程組層級。
public Thread()
public Thread(Runnable target)
public Thread(String name)
public Thread(Runnable target, String name)
public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target, String name)
public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
Runnable 的任務對象。Thread 自身實現了 Runnable,故繼承 Thread 的子類實例也可作為 target。Thread-N(N 為全局遞增計數器)。可後續 setName() 修改。interrupt()、stop() 已棄用)。StackOverflowError,過大浪費內存。package com.example.thread;
public class ThreadExtendDemo {
static class WorkerThread extends Thread {
private final String taskId;
// 構造器鏈:調用父類 Thread(String name)
public WorkerThread(String taskId, String threadName) {
super(threadName); // 設置線程名
this.taskId = taskId;
}
@Override
public void run() {
System.out.printf("[%s] 開始執行任務 %s%n", getName(), taskId);
for (int i = 1; i <= 5; i++) {
System.out.printf("[%s] 任務 %s 進度 %d/5%n", getName(), taskId, i);
try { Thread.sleep(300); }
catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.printf("[%s] 被中斷,提前退出%n", getName());
return;
}
}
System.out.printf("[%s] 任務 %s 完成%n", getName(), taskId);
}
}
public static void main(String[] args) {
System.out.println("主線程: " + Thread.currentThread().getName());
// 創建並啟動
Thread t1 = new WorkerThread("TASK-001", "Worker-Order");
Thread t2 = new WorkerThread("TASK-002", "Worker-Stock");
Thread t3 = new WorkerThread("TASK-003", "Worker-Notify"); // 無顯式名,用默認
t1.start();
t2.start();
t3.start();
// 等待結束
try { t1.join(); t2.join(); t3.join(); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
System.out.println("所有工作線程結束,主線程退出");
}
}
start():本地方法,請求 OS 創建新線程,新線程啟動後自動調用 run()。返回即刻,調用者繼續執行。run():普通實例方法,在當前線程同步執行,不創建新線程。直接調用 run() 是常見錯誤。Thread t1 = new Thread(); // "Thread-0"
Thread t2 = new Thread("Custom"); // "Custom"
Thread t3 = new Thread(); // "Thread-1" (計數器遞增)
t3.setName("Renamed"); // 修改名稱
// 注意:setName() 在 start() 前後均可調用,但 start() 後修改可能發生在 run() 讀取名稱之後,產生不可預期行為。
ThreadGroup group = new ThreadGroup("Batch-Group");
Thread t = new Thread(group, () -> {
System.out.println("所屬組: " + Thread.currentThread().getThreadGroup().getName());
}, "Grouped-Thread");
t.start();
// 批量中斷組內所有線程(慎用)
// group.interrupt();
// 請求 256KB 棧(可能被忽略或調整)
Thread t = new Thread(null, () -> deepRecursion(1), "Small-Stack", 256 * 1024);
t.start();
static void deepRecursion(int depth) {
if (depth > 10000) return;
deepRecursion(depth + 1);
}
// 若棧過小會拋出 StackOverflowError
| 限制 | 影響 | 遷移方案 |
|---|---|---|
| 單繼承 | 無法復用業務邏輯基類 | 改實現 Runnable/Callable,組合優於繼承 |
| 任務與線程耦合 | 難以提交線程池、難以復用任務實例 | 剝離 run() 邏輯到獨立 Runnable 類 |
| 無返回值/異常傳播 | run() 返回 void,異常只能靠 UncaughtExceptionHandler | 用 Callable + Future 捕獲異常與結果 |
| 不支援 Lambda | Java 8+ 函數式編程無法直接使用 | Runnable 是函數式接口,完美適配 |
// Thread.start()
public synchronized void start() {
if (threadStatus != 0) throw new IllegalThreadStateException(); // 防止重複 start
group.add(this); // 註冊到線程組
boolean started = false;
try {
start0(); // native 方法,請求 OS 創建線程
started = true;
} finally {
if (!started) group.threadStartFailed(this);
}
}
// Thread.run()
@Override public void run() {
if (target != null) target.run(); // 若構造器傳了 Runnable,委托執行
// 否則子類重寫的 run() 被調用
}
關鍵洞察:Thread 實現了 Runnable,自身可作為 target;start() 同步加鎖防止重複啟動;線程創建失敗會回滾線程組註冊。
interrupt()/stop() 等極少數場景。