spring boot:使用多個線程池實現實現任務的線程池隔離(spring boot 2.3.2)


一,為什么要使用多個線程池?

使用多個線程池,
把相同的任務放到同一個線程池中,
可以起到隔離的作用,避免有線程出錯時影響到其他線程池,
例如只有一個線程池時,
有兩種任務,下單,處理圖片,
如果線程池被處理圖片的任務占滿,影響下單任務的進行

 

說明:劉宏締的架構森林是一個專注架構的博客,地址: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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM