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

Java 多線程實現方式全解析:Thread、Runnable、Callable 與線程池最佳實踐

     編輯:Java 相關知識

三大核心實現方式對比

維度繼承 Thread實現 Runnable實現 Callable
返回值無無支援泛型返回值
拋出異常不可拋受檢異常不可拋受檢異常可拋受檢異常
線程邏輯復用差(繼承限制)優(同一 Runnable 可給多 Thread)優
適用場景簡單獨立任務大多數業務場景需結果、需異常處理的異步任務

代碼範本:標準化寫法

1. 繼承 Thread(不推薦常規業務)

public class PrintTask extends Thread {
    private final int limit;
    
    public PrintTask(int limit) {
        this.limit = limit;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < limit; i++) {
            System.out.println(getName() + ": " + i);
        }
    }
}

// 使用
new PrintTask(100).start();

2. 實現 Runnable(推薦基礎寫法)

public class PrintRunnable implements Runnable {
    private final int limit;
    
    public PrintRunnable(int limit) {
        this.limit = limit;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < limit; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

// 使用:同一實例可給多線程,共享狀態
PrintRunnable task = new PrintRunnable(100);
new Thread(task, "Thread-A").start();
new Thread(task, "Thread-B").start();

3. 實現 Callable + FutureTask(需結果、異常處理)

public class SumCallable implements Callable<Integer> {
    private final int n;
    
    public SumCallable(int n) {
        this.n = n;
    }
    
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return sum;
    }
}

// 使用
Callable<Integer> callable = new SumCallable(100);
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask, "CalcThread").start();

try {
    Integer result = futureTask.get(); // 阻塞獲取結果
    System.out.println("Sum: " + result);
} catch (InterruptedException | ExecutionException e) {
    // 處理中斷或任務內部異常
    e.printStackTrace();
}

進階:線程池才是生產環境標準

手動 new Thread() 的弊端:頻繁創建銷毀開銷大、無法控制併發數、異常處理分散。

ExecutorService 標準用法

// 1. 固定大小線程池(CPU 密集型推薦)
ExecutorService fixedPool = Executors.newFixedThreadPool(
    Runtime.getRuntime().availableProcessors()
);

// 2. 緩存線程池(IO 密集型、任務短小)
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 3. 單線程串行(保證順序)
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// 4. 定時/周期任務
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

// 提交 Runnable
fixedPool.execute(() -> System.out.println("Runnable task"));

// 提交 Callable,獲取 Future
Future<String> future = fixedPool.submit(() -> {
    Thread.sleep(1000);
    return "Result";
});

try {
    System.out.println(future.get(2, TimeUnit.SECONDS));
} catch (TimeoutException e) {
    future.cancel(true); // 超時取消
}

// 關閉線程池(優雅關閉)
fixedPool.shutdown();
try {
    if (!fixedPool.awaitTermination(60, TimeUnit.SECONDS)) {
        fixedPool.shutdownNow(); // 強制關閉
    }
} catch (InterruptedException e) {
    fixedPool.shutdownNow();
    Thread.currentThread().interrupt();
}

生產環境自定義線程池(拒絕 Executors 預設)

// 阿里巴巴開發手冊強制要求:生產環境不允許使用 Executors 創建線程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    4,                    // corePoolSize
    8,                    // maximumPoolSize
    60L, TimeUnit.SECONDS, // keepAliveTime
    new LinkedBlockingQueue<>(1000), // 有界隊列,防止 OOM
    new ThreadFactoryBuilder().setNameFormat("order-process-%d").build(), // 有意義線程名
    new ThreadPoolExecutor.AbortPolicy() // 拒絕策略:拋異常,便於監控報警
);

關鍵參數解釋:

  • corePoolSize:常駐線程數,即使閒置也不銷毀(除非 allowCoreThreadTimeOut(true))
  • maximumPoolSize:隊列滿後最大擴容線程數
  • workQueue:必須有界,無界隊列會導致 maximumPoolSize 失效,直接 OOM
  • ThreadFactory:命名規範如 biz-module-%d,排查堆棧神器
  • RejectedExecutionHandler:AbortPolicy 拋異常最利於發現問題;CallerRunsPolicy 由調用線程執行,適合不允許丟失任務場景

完整圖解:任務提交到執行流程

submit(task)
    │
    ▼
corePoolSize 未滿? ──是──▶ 創建核心線程執行
    │ 否
    ▼
workQueue 未滿? ──是──▶ 入隊等待
    │ 否
    ▼
maximumPoolSize 未滿? ──是──▶ 創建非核心線程執行
    │ 否
    ▼
執行 RejectedExecutionHandler 拒絕策略

避坑指南

  1. Runnable 共享狀態需加鎖:同一實例給多線程時,實例變量非線程安全
  2. Future.get() 必須設超時:防止任務卡死導致調用線程永久阻塞
  3. 線程池異步任務異常不會自動拋出:必須 future.get() 捕獲 ExecutionException 或使用 Thread.setDefaultUncaughtExceptionHandler
  4. ThreadLocal 配合線程池需手動清理:線程復用會導致 ThreadLocal 洩漏,用完 remove()

總結

業務代碼首選 Runnable + 自定義線程池;需返回值用 Callable + Future;定時任務用 ScheduledExecutorService。拒絕 new Thread()、拒絕 Executors 預設工廠、拒絕無界隊列,這是高併發穩定系統的基石。

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