- 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.ArrayBlockingQueue
、java.util.concurrent.LinkedBlockingQueue
和 java.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. 最終最佳化程式碼
- 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();
}
}
- 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();
}
}