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

Java 多線程實現兩大核心方式深度解析與最佳實踐

     編輯:Java 相關知識

核心方式對比

維度繼承 Thread實現 Runnable
本質Thread 本身實現了 Runnable純粹任務定義,與執行緒分離
繼承限制單繼承,無法繼承其他業務類無限制,可繼承任意類
資源共享較難(需額外傳遞共享對象)天然支援:多執行緒共享同一 Runnable 實例
適用場景簡單獨立任務、教學演示生產環境標準寫法、線程池提交

方式一:繼承 Thread 類

public class DownloadThread extends Thread {
    private final String fileName;
    
    public DownloadThread(String fileName) {
        this.fileName = fileName;
        setName("Downloader-" + fileName); // 設定執行緒名稱,利於除錯
    }
    
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 開始下載: " + fileName);
        try {
            Thread.sleep(2000); // 模擬下載耗時
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 保留中斷狀態
            return;
        }
        System.out.println(Thread.currentThread().getName() + " 下載完成: " + fileName);
    }
    
    public static void main(String[] args) {
        new DownloadThread("report.pdf").start();
        new DownloadThread("image.png").start();
    }
}

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

public class DownloadTask implements Runnable {
    private final String fileName;
    
    public DownloadTask(String fileName) { this.fileName = fileName; }
    
    @Override
    public void run() {
        String tName = Thread.currentThread().getName();
        System.out.println(tName + " 開始下載: " + fileName);
        try { Thread.sleep(2000); } 
        catch (InterruptedException e) { Thread.currentThread().interrupt(); return; }
        System.out.println(tName + " 下載完成: " + fileName);
    }
    
    public static void main(String[] args) {
        // 1. 直接 new Thread 包裝
        new Thread(new DownloadTask("doc.txt"), "Worker-1").start();
        new Thread(new DownloadTask("data.csv"), "Worker-2").start();
        
        // 2. 線程池提交(生產環境標準)
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new DownloadTask("big.zip"));
        pool.submit(new DownloadTask("small.txt"));
        pool.shutdown();
    }
}

進階:Callable + Future 獲取返回值

public class CalcTask implements Callable<Integer> {
    private final int n;
    public CalcTask(int n) { this.n = n; }
    
    @Override public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) sum += i;
        Thread.sleep(100); // 模擬計算
        return sum;
    }
    
    public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newSingleThreadExecutor();
        Future<Integer> f1 = es.submit(new CalcTask(100));
        Future<Integer> f2 = es.submit(new CalcTask(200));
        System.out.println("1+...+100 = " + f1.get()); // 阻塞等待結果
        System.out.println("1+...+200 = " + f2.get());
        es.shutdown();
    }
}

關鍵最佳實踐

  1. 優先使用線程池:避免頻繁創建/銷毀執行緒,控制併發數,支援任務隊列與拒絕策略。
  2. 正確處理中斷:捕獲 InterruptedException 後調用 Thread.currentThread().interrupt() 保留中斷標誌。
  3. 共享資料同步:使用 synchronized、Lock、Atomic*、Concurrent* 等工具,避免競態條件。
  4. 執行緒名稱語義化:ThreadFactoryBuilder.setNameFormat("order-process-%d"),便於日誌追蹤與監控。
  5. 避免在 run() 中吞沒異常:統一使用 Thread.setDefaultUncaughtExceptionHandler 或 Future.get() 捕獲。
  6. 優先用 CompletableFuture (Java 8+):鏈式組合、非阻塞回調、異常處理更優雅。

CompletableFuture 快速示例

CompletableFuture.supplyAsync(() -> fetchUser(1001), executor)
    .thenApply(User::getOrders)
    .thenCompose(orders -> CompletableFuture.supplyAsync(() -> enrich(orders), executor))
    .whenComplete((result, ex) -> {
        if (ex != null) log.error("失敗", ex);
        else log.info("結果: {}", result);
    });
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved