專案中初始化通用執行緒池
說明:在專案初始階段,有的時候需要維護一個通用的執行緒池,用來非同步執行寫操作,不影響主執行緒。直接上程式碼吧!
package com.fy.agent.api.config.thread;
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;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author : lqf
* @description : 通用執行緒池,用於非同步執行寫操作不影響主執行緒
* @date : Create in 10:47 2018/6/1
*/
@Configuration
@EnableAsync
public class InitThread {
//執行緒池維護執行緒的最少數量
private static final int CORE_POOL_SIZE = 10;
//執行緒池維護執行緒的最大數量
private static final int MAX_POOL_SIZE = 50;
//快取佇列
private static final int QUEUE_CAPACITY = 10;
//允許的空閒時間
private static final int KEEP_ALIVE = 60;
@Bean
public Executor myExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix("executor-" );
/*
* 使用此策略,如果新增到執行緒池失敗,那麼主執行緒會自己去執行該任務,不會等待執行緒池中的執行緒去執行
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setKeepAliveSeconds(KEEP_ALIVE);
executor.initialize();
return executor;
}
}
這裡想對拒絕策略RejectedExecutionHandler做一下詳細的介紹。
在使用執行緒池並且使用有界佇列的時候,如果佇列滿了,任務新增到執行緒池的時候就會有問題,針對這些問題java執行緒池提供了以下幾種策略:
- AbortPolicy
- DiscardPolicy
- DiscardOldestPolicy
- CallerRunsPolicy
- 自定義
◇AbortPolicy
該策略是執行緒池的預設策略。使用該策略時,如果執行緒池佇列滿了丟掉這個任務並且丟擲RejectedExecutionException異常。
原始碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//不做任何處理,直接丟擲異常
throw new RejectedExecutionException("Task" + r.toString() +
" rejected from " +
e.toString());
}
◇DiscardPolicy
這個策略和AbortPolicy的slient版本,如果執行緒池佇列滿了,會直接丟掉這個任務並且不會有任何異常。
原始碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//就是一個空的方法
}
◇DiscardOldestPolicy
這個策略從字面上也很好理解,丟棄最老的。也就是說如果佇列滿了,會將最早進入佇列的任務刪掉騰出空間,再嘗試加入佇列。
因為佇列是隊尾進,隊頭出,所以隊頭元素是最老的,因此每次都是移除對頭元素後再嘗試入隊。
原始碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//移除隊頭元素
e.getQueue().poll();
//再嘗試入隊
e.execute(r);
}
}
CallerRunsPolicy
使用此策略,如果新增到執行緒池失敗,那麼主執行緒會自己去執行該任務,不會等待執行緒池中的執行緒去執行。就像是個急脾氣的人,我等不到別人來做這件事就乾脆自己幹。
原始碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//直接執行run方法
r.run();
}
}
自定義
如果以上策略都不符合業務場景,那麼可以自己定義一個拒絕策略,只要實現RejectedExecutionHandler介面,並且實現rejectedExecution方法就可以了。具體的邏輯就在rejectedExecution方法裡去定義就OK了。
例如:我定義了我的一個拒絕策略,叫做MyRejectPolicy,裡面的邏輯就是列印處理被拒絕的任務內容
public class MyRejectPolicy implements RejectedExecutionHandler{
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//Sender是我的Runnable類,裡面有message欄位
if (r instanceof Sender) {
Sender sender = (Sender) r;
//直接列印
System.out.println(sender.getMessage());
}
}
}
這幾種策略沒有好壞之分,只是適用不同場景,具體哪種合適根據具體場景和業務需要選擇,如果需要特殊處理就自己定義好了