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

Java 多線程實現的三種核心方式深度對比與最佳實踐

     編輯:Java 相關知識

前言

多線程是 Java 並發編程的基石。掌握繼承 Thread、實現 Runnable、實現 Callable+Future 三種核心創建方式,理解它們的異同、優缺點及適用場景,是寫出高質量並發代碼的前提。本文深度對比三種方式,並延伸至線程池、CompletableFuture 等現代最佳實踐。

方式一:繼承 Thread 類


public class MyThread extends Thread {
    @Override
    public void run() {
        // 線程執行邏輯
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
    }
    
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start(); // 必須調用 start() 啟動新線程,JVM 會調用 run()
        t2.start();
        // 直接調用 t1.run() 是單線程順序執行!
    }
}
  • 優點:代碼簡單直觀,this 即線程對象,易獲取線程名/ID。
  • 缺點:Java 單繼承限制,無法繼承業務基類;任務與線程耦合,不利於線程池復用;無返回值、無拋檢查異常。
  • 適用:簡單測試、原型驗證、極少數需繼承 Thread 擴展功能場景。

方式二:實現 Runnable 接口 (經典標準)


public class MyTask implements Runnable {
    private final String taskName;
    
    public MyTask(String taskName) { this.taskName = taskName; }
    
    @Override
    public void run() {
        System.out.println(taskName + " running in " + Thread.currentThread().getName());
    }
    
    public static void main(String[] args) {
        MyTask task1 = new MyTask("Task-A");
        MyTask task2 = new MyTask("Task-B");
        
        // 必須包裝進 Thread 對象啟動
        new Thread(task1).start();
        new Thread(task2).start();
        
        // 或提交給線程池 (推薦)
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(task1);
        pool.submit(task2);
        pool.shutdown();
    }
}
  • 優點:任務與線程解耦,同一任務可提交給多線程/線程池;無單繼承限制;支援 Lambda 表達式 () -> { ... }。
  • 缺點:無返回值,無法拋出受檢異常 (run() 簽名無 throws)。
  • 適用:絕大多數業務任務、線程池提交、異步事件處理。

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


import java.util.concurrent.*;

public class MyCallable implements Callable {
    private final int id;
    
    public MyCallable(int id) { this.id = id; }
    
    @Override
    public String call() throws Exception {
        Thread.sleep(1000); // 模擬耗時
        if (id == 3) throw new RuntimeException("Task 3 failed!");
        return "Result of Task-" + id;
    }
    
    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        List> futures = new ArrayList<>();
        
        for (int i = 1; i <= 5; i++) {
            Future f = pool.submit(new MyCallable(i)); // 提交返回 Future
            futures.add(f);
        }
        
        // 獲取結果 (阻塞直到完成)
        for (Future f : futures) {
            try {
                System.out.println("Got: " + f.get()); // 獲取返回值或拋出異常
            } catch (ExecutionException e) {
                System.err.println("Task failed: " + e.getCause().getMessage());
            }
        }
        pool.shutdown();
    }
}
  • 優點:有返回值 (泛型),可拋受檢異常,配合 Future 可異步獲取結果/取消/超時控制。
  • 缺點:API 較繁瑣 (get() 阻塞、需處理 InterruptedException/ExecutionException)。
  • 適用:需要結果聚合、異步計算、超時控制、取消任務 的場景。

三種方式核心對比

維度extends Threadimplements Runnableimplements Callable
返回值無無有 (泛型)
拋受檢異常無無可拋
單繼承限制受限無無
任務/線程耦合強耦合解耦解耦
線程池適配差完美完美
Lambda 支持無完美完美
結果獲取方式共享變量/回調共享變量/回調Future.get()

現代最佳實踐:線程池 + CompletableFuture (JDK 8+)

不再手動創建 Thread! 生產環境統一使用線程池管理線程生命週期,配合 CompletableFuture 進行異步編排。

1. 線程池標準創建 (阿里巴巴開發手冊強制)


// 自定義線程池,拒絕默認無界隊列 (OOM 風險)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100), // 有界隊列
    new ThreadFactoryBuilder().setNameFormat("biz-%d").build(), // 命名便於排查
    new ThreadPoolExecutor.AbortPolicy() // 拒絕策略
);

2. CompletableFuture 異步編排


// 供給異步任務
CompletableFuture f1 = CompletableFuture.supplyAsync(() -> {
    return heavyCompute("A");
}, executor);

// 異步回調鏈
CompletableFuture f2 = f1.thenApply(res -> res.toUpperCase())
                               .thenAccept(System.out::println)
                               .exceptionally(ex -> { log.error("Failed", ex); return null; });

// 多任務組合
CompletableFuture.allOf(f1, f2, f3).join(); // 等待全部完成
CompletableFuture.anyOf(f1, f2).join();     // 等待任意一個完成
  • 優勢:非阻塞鏈式調用、異常統一處理、組合式編排、響應式風格、完美整合線程池。

線程安全核心原則 (無論哪種方式)

  1. 不可變對象:final 字段、無 setter、防禦性拷貝。
  2. 線程封閉:ThreadLocal、棧封閉、Ad-hoc 封閉。
  3. 同步機制:synchronized / Lock / Atomic* / Concurrent* 集合。
  4. 安全發布:volatile、final、安全構造、工廠模式。

總結

  1. 拋棄 extends Thread,耦合度高、擴展性差。
  2. 首選 implements Runnable + 線程池,任務解耦、通用性最強。
  3. 需返回值/異常/取消/超時 → Callable + Future 或 CompletableFuture。
  4. 生產環境禁用 Executors.xxx 默認工廠 (無界隊列 OOM 風險),**自定義 ThreadPoolExecutor**。
  5. JDK 8+ 直接用 CompletableFuture,開啟現代異步編程新姿勢。

理解三種方式本質,是為了在實際架構中做正確選型:**任務提交給線程池,結果用 Future/CompletableFuture 接收,狀態用同步工具守護**。這才是 Java 並發工程化的正確打開方式。

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