本文為博主原創,轉載請注明出處:
1. 使用 ThreadPoolTaskExecutor 封裝自定義配置的線程池Bean
ThreadPoolTaskExecutor 是Spring 中封裝的一個類,spring boot中常用 ThreadPoolTaskExecutor 創建線程池,並把它注入到 IOC 容器中,從而可以全局進行使用。
如下為使用 ThreadPoolTaskExecutor 創建的自定義配置的 線程池類:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; @Configuration @EnableAsync public class ThreadPoolExecutorConfig { @Bean(name="threadPoolExecutor") public Executor threadPoolExecutor(){ ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor(); int processNum = Runtime.getRuntime().availableProcessors(); // 返回可用處理器的Java虛擬機的數量
int corePoolSize = (int) (processNum / (1 - 0.2)); int maxPoolSize = (int) (processNum / (1 - 0.5)); threadPoolExecutor.setCorePoolSize(corePoolSize); // 核心池大小
threadPoolExecutor.setMaxPoolSize(maxPoolSize); // 最大線程數
threadPoolExecutor.setQueueCapacity(maxPoolSize * 1000); // 隊列程度
threadPoolExecutor.setThreadPriority(Thread.MAX_PRIORITY); threadPoolExecutor.setDaemon(false); threadPoolExecutor.setKeepAliveSeconds(300);// 線程空閑時間
threadPoolExecutor.setThreadNamePrefix("test-Executor-"); // 線程名字前綴
return threadPoolExecutor; } }
@EnableAsync是與@Async配合使用,用於執行異步任務
使用示例:
@Service public class SpringExecutorTest { @Autowired private Executor threadPoolExecutor; public void test(){ AtomicInteger num = new AtomicInteger(0); for (int i = 0; i < 5; i++) { threadPoolExecutor.execute(()->{ num.incrementAndGet(); }); } System.out.println(num.get()); } }
2. 與 @Async 注解使用
@Async("threadPoolExecutor") public void asyncTest(){ log.error("threadPoolExecutor asyncTest start"); }
需要注意的是,在使用 @Async 注解時,想使用自定義的 Executor 線程池配置,則需要在 @Async 注解上聲明線程池的名稱,否則會使用默認的線程池配置。
若想在使用@Async 注解時,不顯示聲明線程池,且使用定義線程池的配置,可采用以下方式進行配置:
import lombok.extern.slf4j.Slf4j; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.lang.reflect.Method; import java.util.concurrent.Executor; @Slf4j @Configuration public class NativeAsyncTaskExecutePool implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor(); int processNum = Runtime.getRuntime().availableProcessors(); // 返回可用處理器的Java虛擬機的數量 int corePoolSize = (int) (processNum / (1 - 0.2)); int maxPoolSize = (int) (processNum / (1 - 0.5)); threadPoolExecutor.setCorePoolSize(corePoolSize); // 核心池大小 threadPoolExecutor.setMaxPoolSize(maxPoolSize); // 最大線程數 threadPoolExecutor.setQueueCapacity(maxPoolSize * 1000); // 隊列程度 threadPoolExecutor.setThreadPriority(Thread.MAX_PRIORITY); threadPoolExecutor.setDaemon(false); threadPoolExecutor.setKeepAliveSeconds(300);// 線程空閑時間 threadPoolExecutor.setThreadNamePrefix("test-Executor-"); // 線程名字前綴 return threadPoolExecutor; } /** * 異步任務中異常處理 * @return */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) { log.error("=========================="+arg0.getMessage()+"=======================", arg0); log.error("exception method:"+arg1.getName()); } }; } }
當使用以上的方式時,可在使用 @Async 注解時,不用顯示生命線程池的方式就可以使用自定義的線程池。
3.如何設置核心線程數
對於ThreadPoolTaskExecutor
的corePoolSize
,一般來說可以根據任務的性質、數量、執行時間等因素進行靈活調整,具體的配置需要根據實際情況來決定。這里提供一些核心線程數設置的建議:
- 根據任務的性質來設置
corePoolSize
。
如果任務量較少且每個任務都非常耗時,可以適當減少核心線程數以節省資源,例如將corePoolSize
設置為2-3;如果有大量的耗時短的任務,可以適當增加核心線程數,例如將corePoolSize
設置為10-20等。
- 根據CPU核心數來設置
corePoolSize
。
通常情況下,corePoolSize
建議設置為CPU核心數的2倍,這樣可以保證資源的最大利用。但需要注意的是,當任務的處理時間較長時(例如IO操作),可以適當增加corePoolSize
以避免線程空閑等待。
學習ThreadPoolExecutor 更多可以參考這篇文章:Java線程池實現原理及其在美團業務中的實踐