Java 執行緒池最佳化

Jason207010發表於2024-05-27
  • 1. Java 執行緒池最佳化
    • 1.1. 核心執行緒數
    • 1.2. 任務佇列
      • 1.2.1. LinkedBlockingQueue
      • 1.2.2. ArrayBlockingQueue
      • 1.2.3. SynchronousQueue
      • 1.2.4. 總結
    • 1.3. 最大執行緒數
    • 1.4. 執行緒存活時間
    • 1.5. 拒絕策略
      • 1.5.1. AbortPolicy(預設策略)
      • 1.5.2. CallerRunsPolicy
      • 1.5.3. DiscardPolicy
      • 1.5.4. DiscardOldestPolicy
      • 1.5.5. 總結
    • 1.6. 最終最佳化程式碼

1. Java 執行緒池最佳化

1.1. 核心執行緒數

  • CPU 密集型執行緒池計算公式:
核心執行緒數 = CPU 核心數 + 1
  • IO 密集型執行緒池計算公式:
核心執行緒數 = ((執行緒等待時間 + 執行緒 CPU 時間)/執行緒 CPU 時間)* CPU 數目

1.2. 任務佇列

java.util.concurrent.BlockingQueue 的子類大多可作為任務佇列,但一般使用 java.util.concurrent.ArrayBlockingQueuejava.util.concurrent.LinkedBlockingQueuejava.util.concurrent.SynchronousQueue 作為任務佇列。

1.2.1. LinkedBlockingQueue

基於連結串列的阻塞佇列。

呼叫 LinkedBlockingQueue(int capacity) 構建方法指定佇列大小時,作為有界任務佇列。

呼叫 LinkedBlockingQueue() 無參構建方法時,等價於 LinkedBlockingQueue(Integer.MAX_VALUE),佇列大小為 2147483647,一般任務數量達到這個數值時,程式已經 OOM 了,所以相當於無界任務佇列,本質上還是有界任務佇列。

1.2.2. ArrayBlockingQueue

基於陣列的阻塞佇列。構造方法必須指定佇列大小,是有界任務佇列。

1.2.3. SynchronousQueue

佇列大小為 0 的阻塞佇列,新增到佇列的任務將立即被處理,當所有核心執行緒處於活動狀態,執行緒池將新建新的執行緒來處理任務,直至達到最大執行緒數。

1.2.4. 總結

一般使用 ArrayBlockingQueue 有界阻塞佇列,佇列大小視實際情況而定,一般取 10000

1.3. 最大執行緒數

當所有核心執行緒均處於活動狀態,並且任務佇列已滿,執行緒池才會新建新的執行緒來處理任務,直至所有執行緒達到最大執行緒數。

最大執行緒數視實際情況而定,一般取核心執行緒數 * 2

1.4. 執行緒存活時間

視實際情況而定,一般一分鐘

1.5. 拒絕策略

當任務佇列已滿,並且執行緒池已達到最大執行緒數量時,提交任務到執行緒池將被拒絕。Java 定義了 4 種拒絕策略。

1.5.1. AbortPolicy(預設策略)

丟擲 RejectedExecutionException 來拒絕新提交的任務。

1.5.2. CallerRunsPolicy

呼叫者所在的執行緒會嘗試執行被拒絕的任務。

1.5.3. DiscardPolicy

不採取任何措施,丟棄無法處理的任務。

1.5.4. DiscardOldestPolicy

丟棄佇列最前面的任務,然後嘗試再次提交被拒絕的任務。

1.5.5. 總結

重要資料處理任務使用 CallerRunsPolicy,避免資料丟失。或者使用 AbortPolicy,並做好異常善後處理。

1.6. 最終最佳化程式碼

  1. IO 密集型執行緒池
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class ThreadPoolConfiguration {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        return ExecutorBuilder.create()
                .setCorePoolSize(availableProcessors * 50)
                .setMaxPoolSize(availableProcessors * 100)
                .setKeepAliveTime(1L, TimeUnit.MINUTES)
                .useArrayBlockingQueue(10_000)
                .setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
                .setThreadFactory(ThreadUtil.createThreadFactory("my-thread-"))
                .build();
    }
}
  1. CPU 密集型執行緒池
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class ThreadPoolConfiguration {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        return ExecutorBuilder.create()
                .setCorePoolSize(availableProcessors + 1)
                .setMaxPoolSize(availableProcessors +1)
                .setKeepAliveTime(1L, TimeUnit.MINUTES)
                .useSynchronousQueue()
                .setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
                .setThreadFactory(ThreadUtil.createThreadFactory("my-thread-"))
                .build();
    }
}

相關文章