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

Java 多線程基礎:Thread 與 Runnable 實現差異與 start() 原理深度解析

     編輯:Java 綜合教學

Java 多線程入門:兩種實現方式、start() vs run() 核心差異與底層原理

多線程是 Java 並發編程的基石。本文通過對比 Thread 繼承與 Runnable 實現、深度剖析 start() 底層機制,建立正確的多線程啟動認知。

一、兩種實現方式對比

方式 1:繼承 Thread 類

public class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(name + " 執行: " + i);
            // 適當休眠觀察交替執行
            try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        }
    }

    public static void main(String[] args) {
        new MyThread("線程-A").start();
        new MyThread("線程-B").start();
        new MyThread("線程-C").start();
    }
}

方式 2:實現 Runnable 介面 (推薦)

public class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(name + " 執行: " + i);
            try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        }
    }

    public static void main(String[] args) {
        // 單一任務實例可被多線程共享
        MyRunnable task = new MyRunnable("任務");
        new Thread(task, "線程-1").start();
        new Thread(task, "線程-2").start();
        new Thread(task, "線程-3").start();
    }
}

核心差異總結

維度繼承 Thread實現 Runnable
繼承限制單繼承,無法繼承其他業務類無繼承限制,可繼承其他類
任務共享每個線程獨立任務實例同一 Runnable 可被多線程共享
邏輯分離線程控制與任務邏輯耦合線程控制與任務邏輯解耦
適用場景簡單獨立任務企業級開發標準、線程池提交

二、start() vs run():關鍵差異實證

錯誤示範:直接呼叫 run()

public static void main(String[] args) {
    MyThread mt1 = new MyThread("A");
    MyThread mt2 = new MyThread("B");
    MyThread mt3 = new MyThread("C");
    
    mt1.run();  // 直接呼叫 run() - 順序執行!
    mt2.run();
    mt3.run();
}

輸出結果:

A 執行: 0 ... A 執行: 9
B 執行: 0 ... B 執行: 9
C 執行: 0 ... C 執行: 9
(三個線程依序順序執行,無並發)

正確示範:呼叫 start()

public static void main(String[] args) {
    new MyThread("A").start();
    new MyThread("B").start();
    new MyThread("C").start();
}

輸出結果:

A 執行: 0
B 執行: 0
C 執行: 0
A 執行: 1
B 執行: 1
... (三個線程交替並發執行,順序不確定)

三、start() 底層原理深度解析

JDK 源碼追蹤 (Thread.java)

public synchronized void start() {
    // 1. 狀態檢查:NEW 狀態才能啟動
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    // 2. 加入線程組
    group.add(this);

    boolean started = false;
    try {
        // 3. 核心:呼叫 native 方法啟動系統線程
        start0();
        started = true;
    } finally {
        if (!started) {
            group.threadStartFailed(this); // 啟動失敗清理
        }
    }
}

// Native 方法:由 JVM 根據 OS 實現 (C++ 代碼)
private native void start0();

關鍵點解析

  1. native 關鍵字:表示此方法由 JVM 通過 JNI 調用作業系統原生代碼實現 (C/C++)
  2. 跨平台差異:
    • Linux:呼叫 pthread_create() 建立 POSIX 線程
    • Windows:呼叫 CreateThread() 或 _beginthreadex()
    • macOS:同 Linux,基於 pthread
  3. JVM 內部動作:
    • 分配線程棧 (預設 1MB,可用 -Xss 調整)
    • 初始化程序計數器、本地變量表
    • 將線程加入調度器佇列,等待 CPU 時間片
    • 最終呼叫 run() 方法執行用戶代碼
  4. 為什麼不能直接呼叫 run()?
    • 直接呼叫只是普通方法調用,在主線程棧幀中執行
    • 無新線程建立,無獨立棧空間,無並發調度
    • 本質上是同步阻塞執行

四、線程生命週期狀態機

public enum State {
    NEW,        // 新建:new Thread(),未 start()
    RUNNABLE,   // 可執行:start() 後,含 Running/Ready
    BLOCKED,    // 阻塞:等待監視器鎖 (synchronized)
    WAITING,    // 等待:Object.wait()、Thread.join()、LockSupport.park()
    TIMED_WAITING, // 限時等待:sleep、wait(timeout)、join(timeout)
    TERMINATED  // 終止:run() 正常返回或拋出未捕獲異常
}

start() 啟動多線程並發執行圖示

run() 順序執行圖示

五、現代 Java 多線程最佳實踐

  1. 優先使用執行緒池:ExecutorService + ThreadPoolExecutor,避免頻繁建立/銷毀線程開銷
  2. 任務提交用 Runnable/Callable:配合 Future 獲取結果,CompletableFuture 異步編排
  3. 避免直接操作 Thread:除非需要精細控制線程屬性 (名稱、優先級、守護線程)
  4. 虛擬線程:JDK 21+ Thread.startVirtualThread() 或 Executors.newVirtualThreadPerTaskExecutor(),輕量級高併發新選擇
  5. 線程命名規範:new Thread(task, "order-process-%d") 便於日誌追蹤與 Arthas 排查

六、常見面試題快速答點

  • Q: start() 和 run() 差別? A: start() 啟動新線程由 JVM 調度執行 run();run() 直接呼叫是同步方法調用,無新線程。
  • Q: 為什麼要實現 Runnable 不繼承 Thread? A: 避免單繼承限制、任務邏輯與線程控制解耦、支援任務共享與線程池提交。
  • Q: 線程啟動後必定立即執行嗎? A: 否。進入 RUNNABLE 狀態,等待作業系統調度器分配 CPU 時間片。
  • Q: 如何安全停止線程? A: 使用 interrupt() 標誌位 + 迴圈檢查 Thread.interrupted(),避免 stop() (已棄用、不安全)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved