一,為什么要使用多個線程池?
使用多個線程池,
把相同的任務放到同一個線程池中,
可以起到隔離的作用,避免有線程出錯時影響到其他線程池,
例如只有一個線程池時,
有兩種任務,下單,處理圖片,
如果線程池被處理圖片的任務占滿,影響下單任務的進行
說明:劉宏締的架構森林是一個專注架構的博客,地址: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)