0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何正确使用SpringBoot中的线程池

Android编程精选 来源:CSDN博客 作者:如漩涡 2021-09-02 17:14 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

来自丨CSDN

https://blog.csdn.net/m0_37701381/article/details/81072774

使用步骤

先创建一个线程池的配置,让Spring Boot加载,用来定义如何创建一个ThreadPoolTaskExecutor,要使用@Configuration和@EnableAsync这两个注解,表示这是个配置类,并且是线程池的配置类

@Configuration@EnableAsyncpublic 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();

//配置核心线程数

executor.setCorePoolSize(corePoolSize);

//配置最大线程数

executor.setMaxPoolSize(maxPoolSize);

//配置队列大小

executor.setQueueCapacity(queueCapacity);

//配置线程池中的线程的名称前缀

executor.setThreadNamePrefix(namePrefix);

// rejection-policy:当pool已经达到max size的时候,如何处理新任务

// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

//执行初始化

executor.initialize();

return executor;

}

}

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

# 异步线程配置# 配置核心线程数

async.executor.thread.core_pool_size = 5

# 配置最大线程数

async.executor.thread.max_pool_size = 5

# 配置队列大小

async.executor.thread.queue_capacity = 99999

# 配置线程池中的线程的名称前缀

async.executor.thread.name.prefix = async-service-

创建一个Service接口,是异步线程的接口

public interface AsyncService {

/**

* 执行异步任务

* 可以根据需求,自己加参数拟定,我这里就做个测试演示

*/

void executeAsync();

}

实现类

@Servicepublic 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(“可以在这里执行批量插入等耗时的事情”);

logger.info(“end executeAsync”);

}

}

将Service层的服务异步化,在executeAsync()方法上增加注解@Async(“asyncServiceExecutor”),asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,表明executeAsync方法进入的线程池是asyncServiceExecutor方法创建的。

接下来就是在Controller里或者是哪里通过注解@Autowired注入这个Service

@Autowiredprivate AsyncService asyncService;

@GetMapping(“/async”)

public void async(){

asyncService.executeAsync();

}

用postmain或者其他工具来多次测试请求一下

2018-07-16 2247.655 INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2247.655 INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2247.770 INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2247.770 INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2247.816 INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2247.816 INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2248.833 INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2248.834 INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2248.986 INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2248.987 INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

通过以上日志可以发现,[async-service-]是有多个线程的,显然已经在我们配置的线程池中执行了,并且每次请求中,controller的起始和结束日志都是连续打印的,表明每次请求都快速响应了,而耗时的操作都留给线程池中的线程去异步执行;

虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个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

*/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方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后Override了父类的execute、submit等方法,在里面调用showThreadPoolInfo方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;

修改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();

//配置核心线程数

executor.setCorePoolSize(corePoolSize);

//配置最大线程数

executor.setMaxPoolSize(maxPoolSize);

//配置队列大小

executor.setQueueCapacity(queueCapacity);

//配置线程池中的线程的名称前缀

executor.setThreadNamePrefix(namePrefix);

// rejection-policy:当pool已经达到max size的时候,如何处理新任务

// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

//执行初始化

executor.initialize();

return executor;

}

再次启动该工程测试

2018-07-16 2230.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 2230.952 INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2230.953 INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2231.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 2231.353 INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2231.353 INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2231.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 2231.929 INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2231.930 INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

2018-07-16 2232.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 2232.498 INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync

异步线程要做的事情

可以在这里执行批量插入等耗时的事情

2018-07-16 2232.499 INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

注意这一行日志:

2018-07-16 2232.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]

这说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了3个任务,完成了3个,当前有0个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然;

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • spring
    +关注

    关注

    0

    文章

    341

    浏览量

    15776
  • 线程池
    +关注

    关注

    0

    文章

    57

    浏览量

    7315
  • SpringBoot
    +关注

    关注

    0

    文章

    177

    浏览量

    630

原文标题:SpringBoot中的线程池,你真的会用么?

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Linux多线程对比单线程的优势

    在Linux系统线程是操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程的实际运行单位。一个进程可以拥有多个线程,这些
    发表于 12-01 06:11

    如何使用SpringBoot、Vue2.0、MySQL开发一套云诊所系统?

    ​ 如何使用Java语言;前端框架:Vue2.0;后端框架:SpringBoot;数 据 库: MySQL 这些技术开发一套云诊所系统? 技术栈分析: 后端:SpringBoot
    的头像 发表于 11-27 16:02 131次阅读
    如何使用<b class='flag-5'>SpringBoot</b>、Vue2.0、MySQL开发一套云诊所系统?

    高压放大器在铁磁铁电异质结储备计算的前沿探索

    实验名称: 高压放大器 在铁磁铁电异质结系统物理储备计算的应用 实验内容:将信号发生器产生的一段任意波形经高压放大器放大后输入到系统,探测系统的实时输出,并对该系统进行储备计算
    的头像 发表于 11-03 16:14 321次阅读
    高压放大器在铁磁铁电异质结储备<b class='flag-5'>池</b>计算<b class='flag-5'>中</b>的前沿探索

    数据全复用高性能化层设计思路分享

    大家好,本团队此次分享的内容为可实现数据全复用高性能化层设计思路,核心部分主要由以下3个部分组成; 1.SRAM读取模块;——化使用的存储为SRAM 基于SRAM读与写时序,约束化模块读与写
    发表于 10-29 07:10

    rtt建两个线程a和b,怎么确保线程a执行完立刻切到线程b?

    怎么获取从线程开始切换到切换完成用的总的CPU时钟节拍数量?
    发表于 10-10 06:37

    线程删除时遇到断言,是什么原因导致的?

    在一个线程调用线程删除函数删除另外一个线程,这2个线程的优先级是相等的,被删除的线程也是动态创
    发表于 09-12 06:08

    线程调用rt_thread_mdelay()函数卡死的原因?怎么解决?

    线程调用rt_thread_mdelay()函数程序卡死。搞了两天也不知道问题出在哪,怎么解决。 int main(void) { interrupt_config
    发表于 09-11 08:11

    rtth studionano 如何创建动态线程

    有没有大佬,可以说一下为什么静态线程可以正常使用,动态线程怎么也使用不了。 具体需要什么配置才能使用动态线程创建。谢谢!
    发表于 09-11 06:01

    rtt studionano 如何创建动态线程

    有没有大佬,可以说一下为什么静态线程可以正常使用,动态线程怎么也使用不了。 具体需要什么配置才能使用动态线程创建。谢谢!
    发表于 08-22 06:19

    UVC+MSC实现MSC线程未运行的原因?

    我正在尝试使用 EZUSB 运行 UVC + MSC。我有以下内容。但看起来只有 UVC 线程在运行,而 MSC 没有运行。fw 不响应 MSC 命令。我确保 LPM 已被禁用,只是为了检查传感器
    发表于 07-16 07:08

    深度剖析 RT-Thread 线程调度流程

    rt_system_scheduler_start:调度系统第一个线程rt_hw_context_switch_to:初始化上下文切换环境,触发PendSV异常first_thread:系统优先级最高
    的头像 发表于 06-25 18:24 1437次阅读
    深度剖析 RT-Thread <b class='flag-5'>线程</b>调度流程

    线程的安全注意事项

    线程安全是指多个线程同时访问或修改共享资源时,能够保证程序的正确性和可靠性。 开发者选择TaskPool或Worker进行多线程开发时,在TaskPool和Worker的工作
    发表于 06-20 07:49

    TECS OpenStack资源时间同步失败的故障分析

    某运营商TECS OpenStack资源,在当前告警显示“时钟同步失败”,以10分钟整数倍为间隔上报“时间同步失败”告警,持续时间30秒-1分钟不等。
    的头像 发表于 03-03 10:09 846次阅读
    TECS OpenStack资源<b class='flag-5'>池</b>时间同步失败的故障分析

    基于OpenHarmony标准系统的C++公共基础类库案例:ThreadPoll

    1、程序简介该程序是基于OpenHarmony标准系统的C++公共基础类库的线程处理:ThreadPoll。本案例完成如下工作:创建1个线程,设置该
    的头像 发表于 02-10 18:09 600次阅读
    基于OpenHarmony标准系统的C++公共基础类库案例:ThreadPoll

    ISP与IP地址

    ISP可以为用户提供互联网接入、信息内容、应用服务以及其他增值业务。 要知道,在ISP的网络,IP地址几乎可以说不可或缺。IP地址是指一定范围内可供分配的IP地址的集合。通过分配IP地址,设备
    的头像 发表于 12-27 10:41 918次阅读