最近的項目里要手動維護線程池,然后看到一起開發的小伙伴直接用Java了,我堅信Springboot不可能沒這功能,於是查了些資料,果然有,這里給一下。
首先我們都知道@Async
標簽能讓方法異步執行,但是這個標簽用的是Springboot默認的線程池,想自己實現線程池就要在項目里創建一個TaskExecutor或它的子類的Bean,像這樣:
@Bean
public AsyncTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor=new ThreadPoolTaskExecutor();
//加入此頭后此線程池成為系統線程池
threadPoolTaskExecutor.setThreadNamePrefix("Anno-Executor");
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return threadPoolTaskExecutor;
}
其中拒絕策略可以改為手動編寫,像下面這樣:
threadPoolTaskExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
}
});
JDK里提供了四種默認的策略,非常粗暴:
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- CallerRunsPolicy是讓調用方法的線程自己運行。
- AbortPolicy丟棄任務拋出異常。
- DiscardPolicy:丟棄任務不報錯。
- DiscardOldestPolicy:丟棄隊列最前面任務,之后嘗試執行。
順便說一下,查源碼可知,默認的策略是AbortPolicy,也就是最粗暴的那個,不過考慮到通常是不要讓拒絕發生的,這里用粗暴的方案問題不大。為了實現方便配置,可以用yml對其進行配置:
@Component
@Data
@ConfigurationProperties("thread-pool-factory")
public class ThreadPoolFactory {
private int corePoolSize;
private int maxPoolSize;
private int queueCapacity;
private int keepAliveSeconds;
@Bean
public AsyncTaskExecutor threadPoolTaskExecutor(){
//......
}
}
這里@Data
標簽是lambok的標簽,快速生成getter和setter用。以上Component構建好后,可以直接配置:
thread-pool-factory:
#IO密集型應用,線程數為2N+1
corePoolSize: 9
maxPoolSize: 18
queueCapacity: 100
keepAliveSeconds: 120
以上。