springBoot中线程池ThreadPoolExecutor的使用

一、内容为一个Demo,也是记录一下我自己在学习的过程中的记录,防止后面忘记了可以找到看一看(自己写的终归是好看懂写),没有跟项目实战逻辑结合,需要的可以自己敲一遍,知道个流程(介意请右转),毕竟springBoot为我们封装了很多,只需要简单的配置就可以使用。本文内容我也是根据大佬的博客学习敲的(至于这位大佬是不是原创就不是我关心的了,点击跳转


二、下面开始正文
1.创建个springboot项目吧,使用idea的spring Initializr一键创建,可以使用**https://start.aliyun.com/**的url,这个比默认的快一点,默认的会创建失败。(不信邪的亲身经历过的痛苦,阿里云的真香)
诺就如图2.修改下application.yml配置

server:
  port: 8097

# 线程池默认配置
task:
  pool:
    corePoolSize: 5
    maxPoolSize: 5
    keepAliveSeconds: 300
    queueCapacity: 1000

3.创建线程池配置类

@Component
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolConfig {
    private int corePoolSize;
    private int maxPoolSize;
    private int keepAliveSeconds;
    private int queueCapacity;
    /* GetAndSet方法 */
    }

4.创建线程池信息打印类

package com.zhitu.thread.ThreadPool;

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 HO
 * @Package com.zhitu.thread.ThreadPool
 * @Description 创建ThreadPoolTaskExecutor子类 打印线程池使用情况
 * @Date 2020/8/5 13:32
 * @Version 1.0.0
 */

public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    //定义全局日志打印变量
    private static final Logger LOGGER = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);


    //创建方法打印线程池使用情况
    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
        //判断
        if (threadPoolExecutor == null) {
            return;
        }
        //打印线程池使用日志
        LOGGER.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),    //任务总数
                threadPoolExecutor.getCompletedTaskCount(),  //已完成任务数
                threadPoolExecutor.getActiveCount(),   //活跃线程数
                threadPoolExecutor.getQueue().size()   //队列大小
        );
    }


    /**
    *@Description 重写ThreadPoolTaskExecutor的以下六个方法
    *@Param [task]
    *@Return void
    *@Author Mr.Chen
    *@Date 2020/8/5 13:38
    */
    @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);
    }
}

5.创建线程池类

@Configuration
@EnableAsync
public class TaskExecutePool {

    @Autowired
    private TaskThreadPoolConfig taskThreadPoolConfig;

    @Bean
    public Executor myTaskAsyncPool() {
        //创建线程池对象
        //ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //这里使用我们自己创建的线程池对象类,可打印线程池使用日志信息
        VisiableThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //设置线程池属性
        executor.setCorePoolSize(taskThreadPoolConfig.getCorePoolSize());
        executor.setMaxPoolSize(taskThreadPoolConfig.getMaxPoolSize());
        executor.setKeepAliveSeconds(taskThreadPoolConfig.getKeepAliveSeconds());
        executor.setQueueCapacity(taskThreadPoolConfig.getQueueCapacity());
        //设置线程池前缀(可配置到yml文件中进行动态读取)
        executor.setThreadNamePrefix("myExecutor-");

        // setRejectedExecutionHandler:当pool已经达到线程池最大线程max size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

注意:实用配置类读取数据的话,需要在启动类开启读取配置类注解跟异步注解

@EnableAsync //开启异步支持
@SpringBootApplication
@EnableConfigurationProperties({TaskThreadPoolConfig.class})//开启配置类属性支持
public class ThreadApplication {

    public static void main(String[] args) {
        SpringApplication.run(ThreadApplication.class, args);
    }

}

当然也可以接在线程池类使用注解 @Value("${task.pool.corePoolSize}")来获取yml文件的配置数据,
这样就不需要使用创建配置类来获取数据(第三步可省略)
例如:

@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {

    @Value("${task.pool.corePoolSize}")
    private int corePoolSize;
    @Value("${task.pool.maxPoolSize}")
    private int maxPoolSize;
    @Value("${task.pool.queueCapacity}")
    private int queueCapacity;
    @Value("${task.pool.keepAliveSeconds}")
    private int keepAliveSeconds;

    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor");
//        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置
        executor.setKeepAliveSeconds(keepAliveSeconds);
        //配置线程池中的线程的名称前缀  也可以从yml配置读取
        executor.setThreadNamePrefix("myExecutor-");

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

使用这种读取方法的启动类上则不需要加上开启配置读取注解
6.以上操作在springboot项目中的线程池配置就完成了,下面就是controller–>service–>dao的逻辑了。这里简单写个测试
①controller代码:

package com.zhitu.thread.controller;

import com.zhitu.thread.service.AsyncService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author HO
 * @Package com.zhitu.thread.controller
 * @Description
 * @Date 2020/8/5 11:03
 * @Version 1.0.0
 */
@RestController
public class AsyncController {

    protected static final Logger LOGGER = LoggerFactory.getLogger(AsyncController.class);

    @Autowired
    private AsyncService asyncService;

    @RequestMapping("/")
    public String submit() {
        LOGGER.info("start submit");
        //调用service
        this.asyncService.executeAsync();
        //调用结束 打印日志
        LOGGER.info("end submit");

        return "success";
    }

}

②service代码:

package com.zhitu.thread.service;

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

/**
 * @Author HO
 * @Package com.zhitu.thread.service
 * @Description
 * @Date 2020/8/5 10:58
 * @Version 1.0.0
 */
@Service
public class AsyncService {

    protected static final Logger logger = LoggerFactory.getLogger(AsyncService.class);

    @Async("myTaskAsyncPool")//myTaskAsyncPool为线程池配置类里面的方法,如果使用@Value注解读取配置,则应该改为asyncServiceExecutor(同上面代码的方法名称,可以往上查看)
    public void executeAsync() {
        logger.info("start AsyncService");
        try {
        	//逻辑操作
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        logger.info("end AsyncService");
    }
}

7.以上就是配置完成了,可启动项目访问http://localhost:8097/,按F5刷新页面,查看idea控制台信息

531  INFO 46040 --- [nio-8097-exec-1] c.z.thread.controller.AsyncController    : start submit
2020-08-05 13:57:05.531  INFO 46040 --- [nio-8097-exec-1] c.z.t.T.VisiableThreadPoolTaskExecutor   : myExecutor-, 2. do submit,taskCount [40], completedTaskCount [26], activeCount [5], queueSize [9]
2020-08-05 13:57:05.532  INFO 46040 --- [nio-8097-exec-1] c.z.thread.controller.AsyncController    : end submit
2020-08-05 13:57:05.591  INFO 46040 --- [nio-8097-exec-2] c.z.thread.controller.AsyncController    : start submit
2020-08-05 13:57:05.591  INFO 46040 --- [nio-8097-exec-2] c.z.t.T.VisiableThreadPoolTaskExecutor   : myExecutor-, 2. do submit,taskCount [41], completedTaskCount [26], activeCount [5], queueSize [10]
2020-08-05 13:57:05.591  INFO 46040 --- [nio-8097-exec-2] c.z.thread.controller.AsyncController    : end submit
2020-08-05 13:57:05.675  INFO 46040 --- [   myExecutor-2] com.zhitu.thread.service.AsyncService    : end AsyncService
2020-08-05 13:57:05.676  INFO 46040 --- [   myExecutor-2] com.zhitu.thread.service.AsyncService    : start AsyncService
2020-08-05 13:57:05.756  INFO 46040 --- [nio-8097-exec-4] c.z.thread.controller.AsyncController    : start submit
2020-08-05 13:57:05.756  INFO 46040 --- [nio-8097-exec-4] c.z.t.T.VisiableThreadPoolTaskExecutor   : myExecutor-, 2. do submit,taskCount [42], completedTaskCount [27], activeCount [5], queueSize [10]
2020-08-05 13:57:05.757  INFO 46040 --- [nio-8097-exec-4] c.z.thread.controller.AsyncController    : end submit
2020-08-05 13:57:05.844  INFO 46040 --- [   myExecutor-3] com.zhitu.thread.service.AsyncService    : end AsyncService
2020-08-05 13:57:05.844  INFO 46040 --- [   myExecutor-3] com.zhitu.thread.service.AsyncService    : start AsyncService
2020-08-05 13:57:05.951  INFO 46040 --- [nio-8097-exec-3] c.z.thread.controller.AsyncController    : start submit
2020-08-05 13:57:05.951  INFO 46040 --- [nio-8097-exec-3] c.z.t.T.VisiableThreadPoolTaskExecutor   : myExecutor-, 2. do submit,taskCount [43], completedTaskCount [28], activeCount [5], queueSize [10]
2020-08-05 13:57:05.951  INFO 46040 --- [nio-8097-exec-3] c.z.thread.controller.AsyncController    : end submit
2020-08-05 13:57:06.015  INFO 46040 --- [   myExecutor-4] com.zhitu.thread.service.AsyncService    : end AsyncService
2020-08-05 13:57:06.015  INFO 46040 --- [   myExecutor-4] com.zhitu.thread.service.AsyncService    : start AsyncService
2020-08-05 13:57:06.101  INFO 46040 --- [nio-8097-exec-5] c.z.thread.controller.AsyncController    : start submit
2020-08-05 13:57:06.101  INFO 46040 --- [nio-8097-exec-5] c.z.t.T.VisiableThreadPoolTaskExecutor   : myExecutor-, 2. do submit,taskCount [44], completedTaskCount [29], activeCount [5], queueSize [10]
2020-08-05 13:57:06.101  INFO 46040 --- [nio-8097-exec-5] c.z.thread.controller.AsyncController    : end submit
2020-08-05 13:57:06.171  INFO 46040 --- [   myExecutor-5] com.zhitu.thread.service.AsyncService    : end AsyncService
2020-08-05 13:57:06.171  INFO 46040 --- [   myExecutor-5] com.zhitu.thread.service.AsyncService    : start AsyncService
2020-08-05 13:57:06.275  INFO 46040 --- [nio-8097-exec-6] c.z.thread.controller.AsyncController    : start submit
2020-08-05 13:57:06.275  INFO 46040 --- [nio-8097-exec-6] c.z.t.T.VisiableThreadPoolTaskExecutor   : myExecutor-, 2. do submit,taskCount [45], completedTaskCount [30], activeCount [5], queueSize [10]
2020-08-05 13:57:06.275  INFO 46040 --- [nio-8097-exec-6] c.z.thread.controller.AsyncController    : end submit

具体的控制台输入内容大家可以自行查看。
主要注意这里的打印语句:

myExecutor-, 2. do submit,taskCount [44], completedTaskCount [29], activeCount [5], queueSize [10]

taskCount:总任务数(就是你点了多少F5)
completedTaskCount:已完成任务数
activeCount:活跃线程数(这个是yml文件中配置的最大线程数,大家可更改配置文件的参数数据进行测试)
queueSize :队列数量(配置文件的最大队列等待数)


好啦 以上就是整个配置线程池的流程,本人也是初学者,菜鸟一枚,在此记录一下,方便以后有需要查看时有自己的思路,有错误之处还请各位大佬海涵,有需要可以去上面原文大佬那里学习。小弟在此谢过!!!!!!!!!!!!!看到现在辛苦了!!!!!!!!!!看不如自己动手敲一!!!!遍!!!!!!!!!拜拜了您嘞!!!!!!!!!!!!!!!!!!!!!!!!!

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot使用线程池可以通过ThreadPoolTaskExecutor类来实现。这个类是在javaThreadPoolExecutor的基础上进行封装的\[1\]。下面是一个使用ThreadPoolTaskExecutor的示例代码: 首先,在pom.xml文件添加spring-boot-starter-web和spring-boot-starter-test依赖。 然后,在配置类定义一个自定义的线程池@Bean,例如callbackThreadPool()方法\[2\]。在这个方法,我们可以设置线程池的核心线程数、最大线程数、最大存活时间、工作队列大小等参数,并且可以自定义拒绝策略。 最后,通过调用callbackThreadPool()方法来获取线程池对象,可以在需要的地方使用这个线程池来执行任务。 总结来说,Spring Boot使用线程池可以通过ThreadPoolTaskExecutor类来创建和配置线程池,然后通过调用方法来获取线程池对象并使用它来执行任务\[3\]。 #### 引用[.reference_title] - *1* *3* [SpringBoot使用线程池](https://blog.csdn.net/qq_24983911/article/details/94722569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Springboot项目如何使用线程池](https://blog.csdn.net/weixin_40516924/article/details/121098799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值