首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 線程創建進階:繼承 Thread 類完整指南與源碼級解析

     編輯:Java 程式設計入門

前言

繼承 java.lang.Thread 是 Java 創建線程的另一種方式。雖然工程實踐推薦 Runnable,但理解 Thread 繼承機制、構造器鏈、線程組、堆棧大小等底層細節,對於閱讀源碼、排查線程異常、設計線程池至關重要。本文深度解析 8 個構造器、線程命名規則、start() 與 run() 語義差異、守護線程、線程組層級。

一、Thread 八大構造器全景

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)

參數詳解

  • target:實現 Runnable 的任務對象。Thread 自身實現了 Runnable,故繼承 Thread 的子類實例也可作為 target。
  • name:線程名。未指定時默認 Thread-N(N 為全局遞增計數器)。可後續 setName() 修改。
  • group:線程組。未指定則繼承父線程的組。線程組構成樹狀結構,支援批量操作(interrupt()、stop() 已棄用)。
  • stackSize:請求的棧大小(字節)。僅作為提示,JVM/OS 可能忽略。x86 默認 1MB(32位)或 1MB+(64位)。設置過小導致 StackOverflowError,過大浪費內存。

二、繼承 Thread 實現線程:完整示例

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("所有工作線程結束,主線程退出");
    }
}

三、關鍵行為深度解析

1. start() vs run()

  • start():本地方法,請求 OS 創建新線程,新線程啟動後自動調用 run()。返回即刻,調用者繼續執行。
  • run():普通實例方法,在當前線程同步執行,不創建新線程。直接調用 run() 是常見錯誤。

2. 線程名分配機制

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() 讀取名稱之後,產生不可預期行為。

3. 線程組演示

ThreadGroup group = new ThreadGroup("Batch-Group");
Thread t = new Thread(group, () -> {
    System.out.println("所屬組: " + Thread.currentThread().getThreadGroup().getName());
}, "Grouped-Thread");
t.start();

// 批量中斷組內所有線程(慎用)
// group.interrupt();

4. 堆棧大小實驗

// 請求 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

四、Thread 繼承的局限性與遷移建議

限制影響遷移方案
單繼承無法復用業務邏輯基類改實現 Runnable/Callable,組合優於繼承
任務與線程耦合難以提交線程池、難以復用任務實例剝離 run() 邏輯到獨立 Runnable 類
無返回值/異常傳播run() 返回 void,異常只能靠 UncaughtExceptionHandler用 Callable + Future 捕獲異常與結果
不支援 LambdaJava 8+ 函數式編程無法直接使用Runnable 是函數式接口,完美適配

五、源碼級關鍵點(JDK 17 参考)

// 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() 同步加鎖防止重複啟動;線程創建失敗會回滾線程組註冊。

六、總結

  • 繼承 Thread 適合教學演示、極簡工具類、需重寫 interrupt()/stop() 等極少數場景。
  • 掌握 8 構造器參數語義、線程命名、線程組、堆棧大小提示。
  • 工程實踐:統一遷移至 Runnable/Callable + 線程池,獲得解耦、復用、監控、返回值、異常處理、函數式支援等全方位優勢。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved