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

Java 創建線程的兩種核心方式深度對比:繼承 Thread vs 實現 Runnable - 為什麼推薦第二種?

     編輯:Java 綜合教學

線程創建方式選擇指南:任務與線程分離的設計智慧

核心結論先行

實現 Runnable 介面 (方式二) 是優選,因為它實現了線程任務與線程對象的解耦,符合單一職責原則、開閉原則,支援線程池、避免繼承限制。

方式一:繼承 Thread 類

代碼示例:四個窗口賣票 (有缺陷版)

class Ticket extends Thread {
    private static int num = 50; // static 共享
    
    @Override
    public void run() {
        while (num > 0) {
            System.out.println(Thread.currentThread().getName() + "...sale..." + num--);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        new Ticket().start(); // 窗口1
        new Ticket().start(); // 窗口2
        new Ticket().start(); // 窗口3
        new Ticket().start(); // 窗口4
    }
}

缺陷分析

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

代碼示例:同一任務對象,多線程執行

class Ticket implements Runnable {
    private int num = 50; // 非 static,單一任務實例內部狀態
    
    @Override
    public void run() {
        while (num > 0) {
            System.out.println(Thread.currentThread().getName() + "...sale..." + num--);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Ticket task = new Ticket(); // 單一任務實例
        
        new Thread(task, "窗口-A").start();
        new Thread(task, "窗口-B").start();
        new Thread(task, "窗口-C").start();
        new Thread(task, "窗口-D").start();
    }
}

優勢總結

問題說明後果
任務與線程綁定Ticket 既是任務邏輯,又是線程控制創建 4 個線程 = 創建 4 個 Ticket 實例 = 4 份邏輯副本
共享狀態靠 staticnum 必須 static 才能共享破壞封裝、污染類空間、繼承時易出錯
無法復用任務想換賣票邏輯必須改類/新建類違反開閉原則
單繼承限制已繼承 Thread,無法繼承業務基類設計受限

第三種方式:實現 Callable + FutureTask (有返回值/拋異常)

Callable task = () -> {
    int sum = 0;
    for (int i = 0; i < 100; i++) sum += i;
    return sum;
};

FutureTask futureTask = new FutureTask<>(task);
new Thread(futureTask).start();

Integer result = futureTask.get(); // 阻塞獲取結果
System.out.println("Sum: " + result);

生產環境終極選擇:線程池

// 固定大小線程池
ExecutorService executor = Executors.newFixedThreadPool(4);

// 提交 Runnable
executor.execute(() -> System.out.println("Task running"));

// 提交 Callable
Future future = executor.submit(() -> "Result");

// 關閉
executor.shutdown();

阿里巴巴開發手冊強制規定:線程資源必須通過線程池提供,不允許顯式 new Thread。

對比總結表

優勢說明價值
任務線程分離Runnable 只管「做什麼」,Thread 只管「怎麼跑」單一職責、高內聚
共享狀態自然同一 task 實例的 num 自然共享,無需 static封裝性好、線程安全可控
支援線程池ExecutorService.execute(runnable)生產級併發控制基石
無繼承限制可繼承業務基類、實現多介面設計靈活
Lambda 友好() -> { ... } 直接作為 Runnable代碼簡潔
維度繼承 Thread實現 RunnableCallable + FutureTask線程池
任務復用差優優優
共享數據靠 static自然自然自然
有返回值麻煩不可支援支援
拋受檢異常不可不可支援支援
線程控制強弱(委託Thread)弱專業
繼承限制有無無無
推薦場景教學/簡單測試業務邏輯封裝需結果/異常所有生產代碼

避坑指南

  • 不要為了共享數據而用 static:用同一 Runnable 實例
  • 不要繼承 Thread 又想復用邏輯:改 Runnable
  • 不要在 run() 中吞異常:用 Callable 或線程池的 UncaughtExceptionHandler
  • 不要手動 new Thread:注入 ThreadPoolTaskExecutor / ExecutorService

設計哲學:「任務」是「做什麼」的邏輯,「線程」是「怎麼跑」的資源。 將兩者分離,是併發編程從入門到專業的分水嶺。Runnable + 線程池,是 Java 併發的標準範式。

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