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

Java 併發編程基石:線程創建的兩種核心方式與進程啟動實戰

     編輯:Java 綜合教學

Java 併發編程基石:線程創建的兩種核心方式與進程啟動實戰

線程是 Java 併發的基本執行單元。本文系統梳理 繼承 Thread 與 實現 Runnable 兩大創建線程方式的本質區別、常見誤區(start() vs run()),並延伸講解 ProcessBuilder / Runtime.exec() 啟動系統進程的底層機制與最佳實踐。

一、Java 進程與線程模型速覽

  • 一個 Java 應用 = 一個 JVM 實例(進程),默認進程名 java.exe/javaw.exe。
  • JVM 啟動會創建多個守護線程(GC、JIT、Signal Dispatcher 等)。
  • 用戶代碼若不主動創建線程,僅有 主線程 (main) 執行任務——單線程編程模型。
  • UI 編程(Swing/JavaFX/Android)中耗時操作必須放入子線程,避免阻塞事件分發線程 (EDT/UI 線程)。

二、方式一:繼承 Thread 類

class MyThread extends Thread {
    private static int counter = 0;
    public MyThread() { counter++; }
    @Override
    public void run() {
        System.out.println("主動創建的第 " + counter + " 個線程,ID: " + Thread.currentThread().getId());
    }
}

// 啟動
new MyThread().start(); // 正確:創建新線程執行 run()
// new MyThread().run(); // 錯誤:主線程直接調用 run(),無新線程產生

關鍵點

  • 必須調用 start():請求 JVM 創建新線程,異步執行 run()。
  • run() 只是普通方法:直接調用不會創建線程,當前線程同步執行。
  • 線程 ID 驗證:Thread.currentThread().getId() 可直觀區分主/子線程。
  • 缺點:Java 單繼承限制,若需繼承業務基類則無法使用。

三、方式二:實現 Runnable 接口(推薦)

class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("子線程 ID: " + Thread.currentThread().getId());
    }
}

// 啟動
Thread thread = new Thread(new MyTask());
thread.start(); // 必須包裹在 Thread 中啟動

核心優勢

  • 任務與執行分離:Runnable 代表「任務」,Thread 代表「執行載體」,符合單一職責原則。
  • 無繼承限制:可繼承其他類,或用 Lambda 表達式 () -> { ... } 簡化代碼(Java 8+)。
  • 易於併發框架集成:ExecutorService、FutureTask、CompletableFuture 均接收 Runnable/Callable。

本質揭秘

Thread 類本身實現了 Runnable,其 run() 默認調用目標 Runnable.run()。繼承 Thread 只是將「任務」與「線程」耦合在一起。

四、線程創建對比總結

維度繼承 Thread實現 Runnable
代碼簡潔度高(單類完成)稍低(需 new Thread)
繼承靈活性差(佔用繼承名額)優(可繼承業務類)
任務復用性差優(同一 Runnable 可提交多線程/線程池)
推薦場景簡單測試、教學演示生產代碼、併發框架集成

五、進階:Java 啟動系統進程(Process)

Java 通過 ProcessBuilder(推薦)或 Runtime.exec() 創建操作系統級進程(非 JVM 線程),適用於調用 Shell、Python、exe 等外部程序。

核心類關係

  • Process:抽象類,代表子進程,提供 getInputStream()、getErrorStream()、waitFor()、destroy()。
  • ProcessBuilder:最終類,構建進程配置(命令、環境變量、工作目錄),start() 返回 ProcessImpl(Process 實現類)。
  • Runtime.exec():內部委託 ProcessBuilder,單例模式獲取 Runtime 實例。

ProcessBuilder 實戰範例

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "ipconfig /all");
// 或 List cmds = Arrays.asList("cmd", "/c", "ipconfig /all");
// 設置工作目錄、環境變量
// pb.directory(new File("C:/temp"));
// pb.environment().put("MY_VAR", "value");

Process process = pb.start();
try (Scanner scanner = new Scanner(process.getInputStream(), "GBK")) {
    while (scanner.hasNextLine()) System.out.println(scanner.nextLine());
}
int exitCode = process.waitFor(); // 等待結束,獲取退出碼

Runtime.exec() 等價寫法

String cmd = "cmd /c ipconfig /all";
Process process = Runtime.getRuntime().exec(cmd); // 僅支持單字符串,需自行拼接

注意:exec(String) 不支持參數數組,含空格路徑易出錯,強烈建議使用 ProcessBuilder(String...) 或 List 形式。

六、最佳實踐清單

  1. 優先用 Runnable + 線程池:Executors.newFixedThreadPool(n) 替代手動 new Thread,控制併發度、復用線程。
  2. 區分 CPU 密集 vs IO 密集:CPU 密集線程數 ≈ 核心數;IO 密集可設為 2×核心數。
  3. 進程輸出流必讀:子進程 stdout/stderr 緩衝區滿會阻塞,必須在單獨線程消費或使用 redirectErrorStream(true) 合併流。
  4. 超時控制:process.waitFor(timeout, TimeUnit.SECONDS) (Java 9+) 防止掛死。
  5. 安全管理器:受限環境下 SecurityManager 可能禁止 exec,需配置權限。

掌握線程創建本質與進程啟動機制,是編寫高性能、穩健 Java 併發應用的基石。實際開發中,請拥抱 ExecutorService、CompletableFuture 等高級併發工具,將精力聚焦於業務邏輯而非底層線程管理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved