<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一篇教程學(xué)會Spring Boot線程池的使用

          共 13530字,需瀏覽 28分鐘

           ·

          2023-08-19 17:16

            Java大聯(lián)盟

            幫助萬千Java學(xué)習(xí)者持續(xù)成長

          關(guān)注


          作者|Muscleheng

          blog.csdn.net/Muscleheng/article/details/81409672


          前言

          最近在做訂單模塊,用戶購買服務(wù)類產(chǎn)品之后,需要進行預(yù)約,預(yù)約成功之后分別給商家和用戶發(fā)送提醒短信。考慮發(fā)短信耗時的情況所以我想用異步的方法去執(zhí)行,于是就在網(wǎng)上看見了Spring的@Async了。

          但是遇到了許多問題,使得@Async無效,也一直沒有找到很好的文章去詳細的說明@Async的正確及錯誤的使用方法及需要注意的地方,這里簡單整理了一下遇見的問題,Sring是以配置文件的形式來開啟@Async,而SpringBoot則是以注解的方式開啟。

          我們可以使用springBoot默認的線程池,不過一般我們會自定義線程池(因為比較靈活),配置方式有:

          1. 使用 xml 文件配置的方式

          2. 使用Java代碼結(jié)合@Configuration進行配置(推薦使用)

          下面分別實現(xiàn)兩種配置方式

          第一步、配置@Async

          一、springBoot啟動類的配置:

          在Spring Boot的主程序中配置@EnableAsync,如下所示:

          @ServletComponentScan
          @SpringBootApplication
          @EnableAsync
          public class ClubApiApplication {
              public static void main(String[] args) {
                  SpringApplication.run(ClubApiApplication.class, args);
              }
          }


          二、Spring XML的配置方式:

          1.applicationContext.xml同目錄下創(chuàng)建文件threadPool.xml文件:

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:task="http://www.springframework.org/schema/task"
              xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                  http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
          >


              <!-- 開啟異步,并引入線程池 -->
              <task:annotation-driven executor="threadPool" />

              <!-- 定義線程池 -->
              <bean id="threadPool"
                  class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">

                  <!-- 核心線程數(shù),默認為1 -->
                  <property name="corePoolSize" value="10" />

                  <!-- 最大線程數(shù),默認為Integer.MAX_VALUE -->
                  <property name="maxPoolSize" value="50" />

                  <!-- 隊列最大長度,一般需要設(shè)置值>=notifyScheduledMainExecutor.maxNum;默認為Integer.MAX_VALUE -->
                  <property name="queueCapacity" value="100" />

                  <!-- 線程池維護線程所允許的空閑時間,默認為60s -->
                  <property name="keepAliveSeconds" value="30" />

                  <!-- 完成任務(wù)自動關(guān)閉 , 默認為false-->
                  <property name="waitForTasksToCompleteOnShutdown" value="true" />

                  <!-- 核心線程超時退出,默認為false -->
                  <property name="allowCoreThreadTimeOut" value="true" />

                  <!-- 線程池對拒絕任務(wù)(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy;默認為后者 -->
                  <property name="rejectedExecutionHandler">
                      <!-- AbortPolicy:直接拋出java.util.concurrent.RejectedExecutionException異常 -->
                      <!-- CallerRunsPolicy:主線程直接執(zhí)行該任務(wù),執(zhí)行完之后嘗試添加下一個任務(wù)到線程池中,可以有效降低向線程池內(nèi)添加任務(wù)的速度 -->
                      <!-- DiscardOldestPolicy:拋棄舊的任務(wù)、暫不支持;會導(dǎo)致被丟棄的任務(wù)無法再次被執(zhí)行 -->
                      <!-- DiscardPolicy:拋棄當(dāng)前任務(wù)、暫不支持;會導(dǎo)致被丟棄的任務(wù)無法再次被執(zhí)行 -->
                      <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
                  </property>
              </bean>
          </beans>

          2.然后在applicationContext.xml中引入threadPool.xml:<import resource="threadPool.xml" />

          <!--如果不使用自定義線程池,可以直接使用下面這段標(biāo)簽-->
          <!-- 
          <task:executor id="WhifExecutor" pool-size="10"/> 
          -->

          <import resource="threadPool.xml" />
          <task:annotation-driven executor="WhifExecutor" />


          第二步:創(chuàng)建兩個異步方法的類,如下所示:

          第一個類(這里模擬取消訂單后發(fā)短信,有兩個發(fā)送短信的方法):

          @Service
          public class TranTest2Service {


              // 發(fā)送提醒短信 1
              @Async
              public void sendMessage1() throws InterruptedException {

                  System.out.println("發(fā)送短信方法---- 1   執(zhí)行開始");
                  Thread.sleep(5000); // 模擬耗時
                  System.out.println("發(fā)送短信方法---- 1   執(zhí)行結(jié)束");
              }

              // 發(fā)送提醒短信 2
              @Async
              public void sendMessage2() throws InterruptedException {

                  System.out.println("發(fā)送短信方法---- 2   執(zhí)行開始");
                  Thread.sleep(2000); // 模擬耗時
                  System.out.println("發(fā)送短信方法---- 2   執(zhí)行結(jié)束");
              }
          }

          第二個類。調(diào)用發(fā)短信的方法 (異步方法不能與被調(diào)用的異步方法在同一個類中,否則無效):

          @Service
          public class OrderTaskServic {
              @Autowired
              private TranTest2Service tranTest2Service;

              // 訂單處理任務(wù)
              public void orderTask() throws InterruptedException {

                  this.cancelOrder(); // 取消訂單
                  tranTest2Service.sendMessage1(); // 發(fā)短信的方法   1
                  tranTest2Service.sendMessage2(); // 發(fā)短信的方法  2

              }

              // 取消訂單
              public void cancelOrder() throws InterruptedException {
                  System.out.println("取消訂單的方法執(zhí)行------開始");
                  System.out.println("取消訂單的方法執(zhí)行------結(jié)束 ");
              }

          }

          經(jīng)過測試得到如下結(jié)果:

          1.沒有使用@Async

          2.使用了@Async

          可以看出,沒有使用@Async方式實現(xiàn)的發(fā)送短信是同步執(zhí)行的,意思就是說第一條發(fā)送之后再發(fā)送第二條,第二條發(fā)送成功之后再給用戶提示,這樣顯然會影響用戶體驗,再看使用了@Async實現(xiàn)的,在執(zhí)行第一個發(fā)送短信方法之后馬上開啟另一個線程執(zhí)行第二個方法,顯然這樣我們的處理速度回快很多。

          使用Java代碼結(jié)合@Configuration注解的配置方式(推薦使用)

          1. 新建一個配置類

          package com.boot.common.conf;

          import java.util.concurrent.ThreadPoolExecutor;

          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.scheduling.annotation.EnableAsync;
          import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

          /**
           * 線程池配置
           * @author zhh
           *
           */

          @Configuration
          @EnableAsync
          public class ThreadPoolTaskConfig {

          /** 
           *   默認情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來之后,就會創(chuàng)建一個線程去執(zhí)行任務(wù),
           *    當(dāng)線程池中的線程數(shù)目達到corePoolSize后,就會把到達的任務(wù)放到緩存隊列當(dāng)中;
           *  當(dāng)隊列滿了,就繼續(xù)創(chuàng)建線程,當(dāng)線程數(shù)量大于等于maxPoolSize后,開始使用拒絕策略拒絕 
           */


              /** 核心線程數(shù)(默認線程數(shù)) */
              private static final int corePoolSize = 20;
              /** 最大線程數(shù) */
              private static final int maxPoolSize = 100;
              /** 允許線程空閑時間(單位:默認為秒) */
              private static final int keepAliveTime = 10;
              /** 緩沖隊列大小 */
              private static final int queueCapacity = 200;
              /** 線程池名前綴 */
              private static final String threadNamePrefix = "Async-Service-";

              @Bean("taskExecutor"// bean的名稱,默認為首字母小寫的方法名
              public ThreadPoolTaskExecutor taskExecutor(){
                  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
                  executor.setCorePoolSize(corePoolSize);   
                  executor.setMaxPoolSize(maxPoolSize);
                  executor.setQueueCapacity(queueCapacity);
                  executor.setKeepAliveSeconds(keepAliveTime);
                  executor.setThreadNamePrefix(threadNamePrefix);

                  // 線程池對拒絕任務(wù)的處理策略
                  // CallerRunsPolicy:由調(diào)用線程(提交任務(wù)的線程)處理該任務(wù)
                  executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
                  // 初始化
                  executor.initialize();
                  return executor;
              }
          }


          2.創(chuàng)建兩個異步方法的類(和之前的類類似僅僅是方法上注解不一樣),如下所示:

          第一個類(這里模擬取消訂單后發(fā)短信,有兩個發(fā)送短信的方法):

          package com.boot.test1.service;

          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.scheduling.annotation.Async;
          import org.springframework.stereotype.Service;

          @Service
          public class TranTest2Service {
              Logger log = LoggerFactory.getLogger(TranTest2Service.class);

              // 發(fā)送提醒短信 1
                  @PostConstruct // 加上該注解項目啟動時就執(zhí)行一次該方法
              @Async("taskExecutor")
              public void sendMessage1() throws InterruptedException {
                  log.info("發(fā)送短信方法---- 1   執(zhí)行開始");
                  Thread.sleep(5000); // 模擬耗時
                  log.info("發(fā)送短信方法---- 1   執(zhí)行結(jié)束");
              }

              // 發(fā)送提醒短信 2
                  @PostConstruct // 加上該注解項目啟動時就執(zhí)行一次該方法
              @Async("taskExecutor")
              public void sendMessage2() throws InterruptedException {

                  log.info("發(fā)送短信方法---- 2   執(zhí)行開始");
                  Thread.sleep(2000); // 模擬耗時
                  log.info("發(fā)送短信方法---- 2   執(zhí)行結(jié)束");
              }
          }

          代碼中的 @Async("taskExecutor") 對應(yīng)我們自定義線程池中的 @Bean("taskExecutor") ,表示使用我們自定義的線程池。

          第二個類。調(diào)用發(fā)短信的方法 (異步方法不能與被調(diào)用的異步方法在同一個類中,否則無效):

          @Service
          public class OrderTaskServic {
              @Autowired
              private TranTest2Service tranTest2Service;

              // 訂單處理任務(wù)
              public void orderTask() throws InterruptedException {

                  this.cancelOrder(); // 取消訂單
                  tranTest2Service.sendMessage1(); // 發(fā)短信的方法   1
                  tranTest2Service.sendMessage2(); // 發(fā)短信的方法  2

              }

              // 取消訂單
              public void cancelOrder() throws InterruptedException {
                  System.out.println("取消訂單的方法執(zhí)行------開始");
                  System.out.println("取消訂單的方法執(zhí)行------結(jié)束 ");
              }

          }

          運行截圖:

          注意看,截圖中的 [nio-8090-exec-1] 是Tomcat的線程名稱

          [Async-Service-1]、[Async-Service-2]表示線程1和線程2 ,是我們自定義的線程池里面的線程名稱,我們在配置類里面定義的線程池前綴:

          private static final String threadNamePrefix = "Async-Service-"; // 線程池名前綴,說明我們自定義的線程池被使用了。


          注意事項

          如下方式會使@Async失效

          • 異步方法使用static修飾

          • 異步類沒有使用@Component注解(或其他注解)導(dǎo)致spring無法掃描到異步類

          • 異步方法不能與被調(diào)用的異步方法在同一個類中

          • 類中需要使用@Autowired或@Resource等注解自動注入,不能自己手動new對象

          • 如果使用SpringBoot框架必須在啟動類中增加@EnableAsync注解


          瀏覽 512
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷高清av免费在线 | 一级a爱视频 | 成人性爱AV | 一级黄色片中文字幕 | 欧美成人高潮一二区在线看 |