一,為什么要使用多個線程池?
使用多個線程池,
把相同的任務放到同一個線程池中,
可以起到隔離的作用,避免有線程出錯時影響到其他線程池,
例如只有一個線程池時,
有兩種任務,下單,處理圖片,
如果線程池被處理圖片的任務占滿,影響下單任務的進行
說明:劉宏締的架構森林是一個專注架構的博客,地址:https://www.cnblogs.com/architectforest
對應的源碼可以訪問這里獲取: https://github.com/liuhongdi/
說明:作者:劉宏締 郵箱: 371125307@qq.com
二,演示項目的相關信息
1,項目地址:
https://github.com/liuhongdi/multithreadpool
2,項目功能說明:
創建了兩個線程池,
一個負責發郵件,
另一個負責處理圖片
實際演示中都是sleep
3,項目結構:如圖:

三,java代碼說明:
1,ThreadPoolConfig.java
@Configuration @EnableAsync public class ThreadPoolConfig { //用來生成縮略圖的線程池 @Bean(name = "imageThreadPool") public ThreadPoolTaskExecutor imageThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 設置核心線程數,它是可以同時被執行的線程數量 executor.setCorePoolSize(2); // 設置最大線程數,緩沖隊列滿了之后會申請超過核心線程數的線程 executor.setMaxPoolSize(10); // 設置緩沖隊列容量,在執行任務之前用於保存任務 executor.setQueueCapacity(50); // 設置線程生存時間(秒),當超過了核心線程出之外的線程在生存時間到達之后會被銷毀 executor.setKeepAliveSeconds(60); // 設置線程名稱前綴 executor.setThreadNamePrefix("imagePool-"); // 設置拒絕策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任務結束后再關閉線程池 executor.setWaitForTasksToCompleteOnShutdown(true); //初始化 executor.initialize(); return executor; } //用來發郵件的線程池 @Bean(name = "emailThreadPool") public ThreadPoolTaskExecutor emailThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 設置核心線程數,它是可以同時被執行的線程數量 executor.setCorePoolSize(2); // 設置最大線程數,緩沖隊列滿了之后會申請超過核心線程數的線程 executor.setMaxPoolSize(10); // 設置緩沖隊列容量,在執行任務之前用於保存任務 executor.setQueueCapacity(50); // 設置線程生存時間(秒),當超過了核心線程出之外的線程在生存時間到達之后會被銷毀 executor.setKeepAliveSeconds(60); // 設置線程名稱前綴 executor.setThreadNamePrefix("emailPool-"); // 設置拒絕策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任務結束后再關閉線程池 executor.setWaitForTasksToCompleteOnShutdown(true); //初始化 executor.initialize(); return executor; } }
說明:配置要使用的線程池,按業務類型區分開,
注意命名:一個命名為:emailThreadPool
一個命名為:imageThreadPool
另外注意線程池使用了不同的前綴,使實際運行時區分
2,HomeController.java
@RequestMapping("/home")
@Controller
public class HomeController {
@Resource
private MailService mailService;
@Resource
private ImageService imageService;
@Resource
private ThreadPoolTaskExecutor imageThreadPool;
//監控線程池的狀態,
//我們得到的數字,只是大體接近,並不是嚴格的准確數字
@GetMapping("/poolstatus")
@ResponseBody
public String poolstatus() {
String statusStr = "";
int queueSize = imageThreadPool.getThreadPoolExecutor().getQueue().size();
statusStr +="當前排隊線程數:" + queueSize;
int activeCount = imageThreadPool.getThreadPoolExecutor().getActiveCount();
statusStr +="當前活動線程數:" + activeCount;
long completedTaskCount = imageThreadPool.getThreadPoolExecutor().getCompletedTaskCount();
statusStr +="執行完成線程數:" + completedTaskCount;
long taskCount = imageThreadPool.getThreadPoolExecutor().getTaskCount();
statusStr +="總線程數:" + taskCount;
return statusStr;
}
//異步發送一封注冊成功的郵件
@GetMapping("/asyncmail")
@ResponseBody
public String regMail() {
mailService.sendHtmlMail();
return "mail sended";
}
//異步執行sleep1秒10次
@GetMapping("/asyncimage")
@ResponseBody
public Map<String, Object> asyncsleep() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
Map<String, Object> map = new HashMap<>();
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 50; i++) {
Future<String> future = imageService.asynctmb(i);
futures.add(future);
}
List<String> response = new ArrayList<>();
for (Future future : futures) {
String string = (String) future.get();
response.add(string);
}
map.put("data", response);
map.put("消耗時間", String.format("任務執行成功,耗時{%s}毫秒", System.currentTimeMillis() - start));
return map;
}
}
3,MailServiceImpl.java
@Service public class MailServiceImpl implements MailService { private Logger logger= LoggerFactory.getLogger(MailServiceImpl.class); @Resource private MailUtil mailUtil; //異步發送html格式的郵件,演示時只是sleep1秒 @Async(value="emailThreadPool") @Override public void sendHtmlMail() { logger.info("sendHtmlMail begin"); try { Thread.sleep(2000); //延時1秒 } catch(InterruptedException e) { e.printStackTrace(); } } }
說明:Async注解指定線程池的名字是:emailThreadPool
4,ImageServiceImpl.java
@Service public class ImageServiceImpl implements ImageService { private Logger logger= LoggerFactory.getLogger(MailServiceImpl.class); //演示處理圖片,只是sleep1秒 @Async(value="imageThreadPool") @Override public Future<String> asynctmb(int i) { logger.info("asynctmb begin"); String start= TimeUtil.getMilliTimeNow(); try { Thread.sleep(1000); //延時1秒 } catch(InterruptedException e) { e.printStackTrace(); } //log.info("async function sleep end"); String end=TimeUtil.getMilliTimeNow(); return new AsyncResult<>(String.format("asynctmb方法,第 %s 個線程:開始時間:%s,結束時間:%s",i,start,end)); } }
說明:Async注解指定線程池的名字是:imageThreadPool
四,測試效果:
1,測試一個線程:訪問:
http://127.0.0.1:8080/home/asyncmail
查看控制台:
2020-08-10 14:54:35.671 INFO 2570 --- [ emailPool-1] c.m.demo.service.impl.MailServiceImpl : sendHtmlMail begin
可以看到線程的前綴是emailThreadPool的線程的前綴
2,測試多個線程:訪問:
http://127.0.0.1:8080/home/asyncimage
可以看到返回信息:
...
"消耗時間":"任務執行成功,耗時{25052}毫秒"
執行時每次並發的線程數是2,一共創建了50個線程,
每個線程sleep用時1秒
所以共用時25秒,
3,查看線程池狀態:訪問:
http://127.0.0.1:8080/home/asyncimage
同時訪問:
http://127.0.0.1:8080/home/poolstatus
可以看到返回的狀態信息:
當前排隊線程數:44當前活動線程數:2執行完成線程數:54總線程數:100
說明:ThreadPoolExecutor中的統計信息只是近似值,
不是完全准確的數字
五,查看spring boot的版本
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.2.RELEASE)
