<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>

          Spring Boot 中的線程池,這也太好用了!

          共 6592字,需瀏覽 14分鐘

           ·

          2021-06-07 15:49


          前言

          前兩天做項目的時候,想提高一下插入表的性能優(yōu)化,因為是兩張表,先插舊的表,緊接著插新的表,一萬多條數(shù)據(jù)就有點慢了。

          后面就想到了線程池ThreadPoolExecutor,而用的是Spring Boot項目,可以用Spring提供的對ThreadPoolExecutor封裝的線程池ThreadPoolTaskExecutor,直接使用注解啟用。

          使用步驟

          先創(chuàng)建一個線程池的配置,讓Spring Boot加載,用來定義如何創(chuàng)建一個ThreadPoolTaskExecutor,要使用@Configuration和@EnableAsync這兩個注解,表示這是個配置類,并且是線程池的配置類。

          Spring Boot 基礎(chǔ)就不介紹了,系列教程和示例源碼看這里:https://github.com/javastacks/spring-boot-best-practice

          @Configuration
          @EnableAsync
          public class ExecutorConfig {

              private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

              @Value("${async.executor.thread.core_pool_size}")
              private int corePoolSize;
              @Value("${async.executor.thread.max_pool_size}")
              private int maxPoolSize;
              @Value("${async.executor.thread.queue_capacity}")
              private int queueCapacity;
              @Value("${async.executor.thread.name.prefix}")
              private String namePrefix;

              @Bean(name = "asyncServiceExecutor")
              public Executor asyncServiceExecutor() {
                  logger.info("start asyncServiceExecutor");
                  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
                  //配置核心線程數(shù)
                  executor.setCorePoolSize(corePoolSize);
                  //配置最大線程數(shù)
                  executor.setMaxPoolSize(maxPoolSize);
                  //配置隊列大小
                  executor.setQueueCapacity(queueCapacity);
                  //配置線程池中的線程的名稱前綴
                  executor.setThreadNamePrefix(namePrefix);

                  // rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時候,如何處理新任務(wù)
                  // CALLER_RUNS:不在新線程中執(zhí)行任務(wù),而是有調(diào)用者所在的線程來執(zhí)行
                  executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
                  //執(zhí)行初始化
                  executor.initialize();
                  return executor;
              }
          }

          @Value是我配置在application.properties,可以參考配置,自由定義

          # 異步線程配置
          # 配置核心線程數(shù)
          async.executor.thread.core_pool_size = 5
          # 配置最大線程數(shù)
          async.executor.thread.max_pool_size = 5
          # 配置隊列大小
          async.executor.thread.queue_capacity = 99999
          # 配置線程池中的線程的名稱前綴
          async.executor.thread.name.prefix = async-service-

          創(chuàng)建一個Service接口,是異步線程的接口

          public interface AsyncService {
              /**
               * 執(zhí)行異步任務(wù)
               * 可以根據(jù)需求,自己加參數(shù)擬定,我這里就做個測試演示
               */
              void executeAsync();
          }

          實現(xiàn)類

          @Service
          public class AsyncServiceImpl implements AsyncService {
              private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

              @Override
              @Async("asyncServiceExecutor")
              public void executeAsync() {
                  logger.info("start executeAsync");

                  System.out.println("異步線程要做的事情");
                  System.out.println("可以在這里執(zhí)行批量插入等耗時的事情");

                  logger.info("end executeAsync");
              }
          }

          將Service層的服務(wù)異步化,在executeAsync()方法上增加注解@Async("asyncServiceExecutor"),asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,表明executeAsync方法進(jìn)入的線程池是asyncServiceExecutor方法創(chuàng)建的。

          接下來就是在Controller里或者是哪里通過注解@Autowired注入這個Service。Spring Boot 學(xué)習(xí)筆記,分享給你。更多 Spring Boot 教程可以微信搜索Java技術(shù)棧在后臺發(fā)送 boot 進(jìn)行閱讀,我都整理好了。

          @Autowired
          private AsyncService asyncService;

          @GetMapping("/async")
          public void async(){
              asyncService.executeAsync();
          }

          用postmain或者其他工具來多次測試請求一下

           2018-07-16 22:15:47.655  INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:15:47.655  INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:15:47.770  INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:15:47.770  INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:15:47.816  INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:15:47.816  INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:15:48.833  INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:15:48.834  INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:15:48.986  INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:15:48.987  INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync

          通過以上日志可以發(fā)現(xiàn),[async-service-]是有多個線程的,顯然已經(jīng)在我們配置的線程池中執(zhí)行了,并且每次請求中,controller的起始和結(jié)束日志都是連續(xù)打印的,表明每次請求都快速響應(yīng)了,而耗時的操作都留給線程池中的線程去異步執(zhí)行;

          雖然我們已經(jīng)用上了線程池,但是還不清楚線程池當(dāng)時的情況,有多少線程在執(zhí)行,多少在隊列中等待呢?這里我創(chuàng)建了一個ThreadPoolTaskExecutor的子類,在每次提交線程的時候都會將當(dāng)前線程池的運行狀況打印出來

          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
          import org.springframework.util.concurrent.ListenableFuture;

          import java.util.concurrent.Callable;
          import java.util.concurrent.Future;
          import java.util.concurrent.ThreadPoolExecutor;

          /**
           * @Author: ChenBin
           */
          public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

              private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);

              private void showThreadPoolInfo(String prefix) {
                  ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

                  if (null == threadPoolExecutor) {
                      return;
                  }

                  logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                          this.getThreadNamePrefix(),
                          prefix,
                          threadPoolExecutor.getTaskCount(),
                          threadPoolExecutor.getCompletedTaskCount(),
                          threadPoolExecutor.getActiveCount(),
                          threadPoolExecutor.getQueue().size());
              }

              @Override
              public void execute(Runnable task) {
                  showThreadPoolInfo("1. do execute");
                  super.execute(task);
              }

              @Override
              public void execute(Runnable task, long startTimeout) {
                  showThreadPoolInfo("2. do execute");
                  super.execute(task, startTimeout);
              }

              @Override
              public Future<?> submit(Runnable task) {
                  showThreadPoolInfo("1. do submit");
                  return super.submit(task);
              }

              @Override
              public <T> Future<T> submit(Callable<T> task) {
                  showThreadPoolInfo("2. do submit");
                  return super.submit(task);
              }

              @Override
              public ListenableFuture<?> submitListenable(Runnable task) {
                  showThreadPoolInfo("1. do submitListenable");
                  return super.submitListenable(task);
              }

              @Override
              public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
                  showThreadPoolInfo("2. do submitListenable");
                  return super.submitListenable(task);
              }
          }

          如上所示,showThreadPoolInfo方法中將任務(wù)總數(shù)、已完成數(shù)、活躍線程數(shù),隊列大小都打印出來了,然后Override了父類的execute、submit等方法,在里面調(diào)用showThreadPoolInfo方法,這樣每次有任務(wù)被提交到線程池的時候,都會將當(dāng)前線程池的基本情況打印到日志中;

          修改ExecutorConfig.java的asyncServiceExecutor方法,將ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改為ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor()

          @Bean(name = "asyncServiceExecutor")
          public Executor asyncServiceExecutor() {
              logger.info("start asyncServiceExecutor");
              //在這里修改
              ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
              //配置核心線程數(shù)
              executor.setCorePoolSize(corePoolSize);
              //配置最大線程數(shù)
              executor.setMaxPoolSize(maxPoolSize);
              //配置隊列大小
              executor.setQueueCapacity(queueCapacity);
              //配置線程池中的線程的名稱前綴
              executor.setThreadNamePrefix(namePrefix);

              // rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時候,如何處理新任務(wù)
              // CALLER_RUNS:不在新線程中執(zhí)行任務(wù),而是有調(diào)用者所在的線程來執(zhí)行
              executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
              //執(zhí)行初始化
              executor.initialize();
              return executor;
          }

          再次啟動該工程測試

          2018-07-16 22:23:30.951  INFO 14088 --- [nio-8087-exec-2] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [0], completedTaskCount [0], activeCount [0], queueSize [0]
          2018-07-16 22:23:30.952  INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:23:30.953  INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:23:31.351  INFO 14088 --- [nio-8087-exec-3] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [1], completedTaskCount [1], activeCount [0], queueSize [0]
          2018-07-16 22:23:31.353  INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:23:31.353  INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:23:31.927  INFO 14088 --- [nio-8087-exec-5] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [2], completedTaskCount [2], activeCount [0], queueSize [0]
          2018-07-16 22:23:31.929  INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:23:31.930  INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
          2018-07-16 22:23:32.496  INFO 14088 --- [nio-8087-exec-7] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]
          2018-07-16 22:23:32.498  INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
          異步線程要做的事情
          可以在這里執(zhí)行批量插入等耗時的事情
          2018-07-16 22:23:32.499  INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync

          注意這一行日志:

          2018-07-16 22:23:32.496  INFO 14088 --- [nio-8087-exec-7] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]

          這說明提交任務(wù)到線程池的時候,調(diào)用的是submit(Callable task)這個方法,當(dāng)前已經(jīng)提交了3個任務(wù),完成了3個,當(dāng)前有0個線程在處理任務(wù),還剩0個任務(wù)在隊列中等待,線程池的基本情況一路了然。

          最后,關(guān)注公眾號Java技術(shù)棧,在后臺回復(fù):面試,可以獲取我整理的 Java、Spring Boot 系列面試題和答案,非常齊全。

          原文鏈接:https://blog.csdn.net/m0_37701381/article/details/81072774

          版權(quán)聲明:本文為CSDN博主「如漩渦」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。


          1、Intellij IDEA這樣 配置注釋模板,讓你瞬間高出一個逼格!
          2、基于SpringBoot的迷你商城系統(tǒng),附源碼!
          3、最牛逼的 Java 日志框架,性能無敵,橫掃所有對手!
          4、把Redis當(dāng)作隊列來用,真的合適嗎?
          5、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          6、全網(wǎng)最全 Java 日志框架適配方案!還有誰不會?
          7、Spring中毒太深,離開Spring我居然連最基本的接口都不會寫了

          點分享

          點收藏

          點點贊

          點在看

          瀏覽 57
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  亚洲丝袜视频 | 中文字幕乱码亚洲中文在线 | 色美女亚洲被日 | 欧美综合在线观看 | 尻屄视频免费观看 |