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

          SpringBoot中的線程池,你真的會用么?

          共 15971字,需瀏覽 32分鐘

           ·

          2021-05-24 17:34

          作者 | 如漩渦

          來源 | blog.csdn.net/m0_37701381/article/details/81072774

          前言

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

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

          使用步驟

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

          @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:當pool已經(jīng)達到max size的時候,如何處理新任務
                  // CALLER_RUNS:不在新線程中執(zhí)行任務,而是有調(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í)行異步任務 * 可以根據(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 層的服務異步化,在executeAsync()方法上增加注解@Async("asyncServiceExecutor"),asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,表明executeAsync方法進入的線程池是asyncServiceExecutor方法創(chuàng)建的

          接下來就是在 Controller 里或者是哪里通過注解@Autowired注入這個 Service

          @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ù)打印的,表明每次請求都快速響應了,而耗時的操作都留給線程池中的線程去異步執(zhí)行;

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

          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 * @Date: 2018/7/16/0016 22:19 */
          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 方法中將任務總數(shù)、已完成數(shù)、活躍線程數(shù),隊列大小都打印出來了,然后 Override 了父類的 execute、submit 等方法,在里面調(diào)用 showThreadPoolInfo 方法,這樣每次有任務被提交到線程池的時候,都會將當前線程池的基本情況打印到日志中;

          修改ExecutorConfig.javaasyncServiceExecutor方法,將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:當pool已經(jīng)達到max size的時候,如何處理新任務
                  // CALLER_RUNS:不在新線程中執(zhí)行任務,而是有調(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-, 2do 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-, 2do 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-, 2do 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-, 2do 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-, 2do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]

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

          1、Intellij IDEA這樣 配置注釋模板,讓你瞬間高出一個逼格!
          2、吊炸天的 Docker 圖形化工具 Portainer,必須推薦給你!
          3、最牛逼的 Java 日志框架,性能無敵,橫掃所有對手!
          4、把Redis當作隊列來用,真的合適嗎?
          5、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          6、全網(wǎng)最全 Java 日志框架適配方案!還有誰不會?
          7、Spring中毒太深,離開Spring我居然連最基本的接口都不會寫了

          點分享

          點收藏

          點點贊

          點在看

          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  男人操女人在线观看 | 成人操B视频| 做爰视频 | 超碰在线青娱乐 | 天天操天天射天天日 |