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

Java 多線程實現的三種核心方式詳解:繼承 Thread、實現 Runnable、實現 Callable 與 FutureTask

     編輯:Java 綜合教學

Java 多線程實現的三種核心方式詳解:繼承 Thread、實現 Runnable、實現 Callable 與 FutureTask

Java 多線程編程是並發編程的基石。掌握三種核心創建線程方式的本質差異、適用場景與進階用法,是構建高性能並發系統的前置技能。

方式一:繼承 Thread 類

public class ThreadExtendsThread extends Thread {
    private int i;
    
    @Override
    public void run() {
        for (; i < 100; i++) {
            System.out.println(getName() + " " + i);
        }
    }
}

// 使用方式
ThreadExtendsThread thread = new ThreadExtendsThread();
thread.start(); // 必須調用 start(),而非 run()

特點:

  • run() 是線程執行體,Thread 對象即線程對象
  • 繼承限制:Java 單繼承,若已繼承其他類則無法使用
  • 字段共享:每個線程對象擁有獨立字段副本,無共享狀態問題
  • 獲取結果困難:需通過共享字段、回調、靜態容器等間接方式

方式二:實現 Runnable 接口

public class ThreadImplementsRunnable implements Runnable {
    private int i;
    
    @Override
    public void run() {
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

// 使用方式
Runnable runnable = new ThreadImplementsRunnable();
new Thread(runnable).start(); // 線程 1
new Thread(runnable).start(); // 線程 2:共享同一 Runnable 實例字段

特點:

  • 解繼承限制:可繼承其他類,實現多接口
  • 同一 Runnable 可被多 Thread 共享:實例字段共享,適合「賣票」等共享資源場景
  • 分離任務與執行:Runnable 定義任務,Thread 負責調度,符合單一職責
  • 無返回值、無拋檢查異常:run() 簽名固定

方式三:實現 Callable 接口 + FutureTask

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class ThreadImplementsCallable implements Callable<Integer> {
    private int i;
    
    @Override
    public Integer call() throws Exception {
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i; // 返回結果
    }
}

// 使用方式
Callable<Integer> callable = new ThreadImplementsCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);

new Thread(futureTask).start(); // 線程 1
new Thread(futureTask).start(); // 線程 2:共享 FutureTask,結果僅計算一次

// 主線程異步獲取結果
try {
    Integer result = futureTask.get(); // 阻塞直到完成
    System.out.println("futureTask return: " + result);
} catch (Exception e) {
    e.printStackTrace();
}

特點:

  • call() 支持返回值:泛型指定返回類型
  • call() 可拋檢查異常:異常傳遞給 future.get()
  • FutureTask 實現 Runnable + Future:既可作為任務提交執行,又可獲取結果
  • 同一 FutureTask 多線程啟動:只執行一次 call(),結果緩存,所有 get() 共享結果
  • 靈活控制:cancel()、isDone()、isCancelled()、get(timeout)

三大方式核心對比

維度繼承 Thread實現 Runnable實現 Callable + FutureTask
繼承限制受單繼承限制無限制無限制
返回值無(靜態/字段傳遞)無✅ 泛型返回值
拋檢查異常無無✅ 可拋 Exception
共享狀態各自獨立✅ 同 Runnable 共享字段✅ 同 FutureTask 共享結果
結果獲取困難困難✅ future.get() 阻塞獲取
取消控制無無✅ future.cancel()
適用場景簡單獨立任務共享資源任務、線程池提交異步計算、有返回值、需異常處理

進階:線程池與 ExecutorService 最佳實踐

生產環境不建議顯式 new Thread(),應使用線程池:

ExecutorService executor = Executors.newFixedThreadPool(4);

// 提交 Runnable
executor.execute(new ThreadImplementsRunnable());

// 提交 Callable,獲取 Future
Future<Integer> future = executor.submit(new ThreadImplementsCallable());
Integer result = future.get(); // 阻塞獲取

// 批量提交
List<Future<Integer>> futures = executor.invokeAll(tasks);

// 關閉
executor.shutdown();

補充:第四種方式——實現 Runnable + 線程池(實質是方式二的生產級用法)

本質上只有三種創建「線程任務」的方式,但執行方式分為:

  1. 顯式 new Thread(task).start()(開發/測試)
  2. ExecutorService.submit/execute(task)(生產標準)
  3. CompletableFuture.supplyAsync(() -> result)(Java 8+ 異步編排)
  4. Virtual Thread (Java 21+):Thread.startVirtualThread(() -> {})

避坑指南

  • 調用 run() 而非 start():直接在主線程同步執行,非多線程
  • Runnable 共享字段未同步:多線程寫入需 volatile、AtomicInteger、synchronized 或 ThreadLocal
  • FutureTask 重複提交:同一 FutureTask 只能提交一次,再次提交拋 IllegalStateException
  • get() 永久阻塞:必須設置超時 get(5, TimeUnit.SECONDS) 防止死等
  • Callable 異常吞噬:future.get() 拋 ExecutionException,真實異常在 getCause()

總結

  • Thread:理解線程本質,簡單場景
  • Runnable:任務與執行分離,線程池標準任務類型
  • Callable + FutureTask:異步計算、返回值、異常傳遞、取消控制——異步編程核心組件
  • 生產必用線程池:ThreadPoolExecutor 精細控制核心/最大線程、隊列、拒絕策略、存活時間

三種方式層層遞進,解決了「如何運行」「任務解耦」「異步結果」三大核心需求,構成了 Java 並發編程的基石三角。

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