热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Springboot应用中线程池配置详细教程(最新2021版)

这篇文章主要介绍了Springboot应用中线程池配置教程(2021版),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

前言:日常开发中我们常用ThreadPoolExecutor提供的线程池服务帮我们管理线程,在Springboot中更是提供了@Async注解来简化业务逻辑提交到线程池中执行的过程。由于Springboot中默认设置的corePoolSize=1和queyeCapacity=Integer.MAX_VALUE,相当于采用单线程处理所有任务,这就与多线程的目的背道而驰,所以这就要求我们在使用@Async注解时要配置线程池。本文就讲述下Springboot应用下的线程池配置。

背景知识:Springboot中通过使用ThreadPoolTaskExecutor这个JavaBean对象的corePoolSize(核心线程数)、maxPoolSize(最大线程数)、keepAliveSeconds(线程空闲时长)和queueCapacity(任务队列容量)属性来配置ThreadPoolExecutor,以上四个属性的作用大致如下:

新提交一个任务时的处理流程很明显:

  1. 如果当前线程池的线程数还没有达到基本大小(poolSize
  2. 如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列未满时,就将新提交的任务提交到阻塞队列排队,等候处理workQueue.offer(command);
  3. 如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列满时;
  • 当前poolSize
  • 当前poolSize=maximumPoolSize,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增的任务,取决于线程池的饱和策略RejectedExecutionHandler。

好了,回到正文。目前配置Springboot线程池主要有两种方式:配置默认线程池和提供自定义线程池;毫无疑问,两种配置方式并无优劣。从使用角度来讲,由于自定义线程池是自定义即没有被Springboot默认使用的线程池,那么就需要通过@Async("自定义线程池bean对象名")的方式去使用,其它地方同默认线程池使用方式一致;下面通过一个简单的Springboot应用结合实际来展示:

1、新建一个Springboot项目,项目结构和pom.xml内容如下:

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

  4.0.0
  
    org.springframework.boot
    spring-boot-starter-parent
    2.1.17.RELEASE
     
  
  com.hugesoft
  springboot-async
  0.0.1
  springboot-async
  Demo project for Spring Boot
  
    1.8
  
  
    
      org.springframework.boot
      spring-boot-starter-web
    
    
      org.projectlombok
      lombok
    
    
      org.springframework.boot
      spring-boot-starter-test
      test
    
  
 
  
    
      
        org.springframework.boot
        spring-boot-maven-plugin
      
    
  
 

二、application.yml中,自定义了线程池需要配置的四个属性,内容如下:

task:
 pool:
  corePoolSize: 10
  maxPoolSize: 20
  keepAliveSeconds: 300
  queueCapacity: 50

三、在com.hugesoft.config包中有三个类:TaskThreadPoolConfig类用来简化封装application.yml配置的属性,OverrideDefaultThreadPoolConfig类提供了配置默认线程池的方式,CustomizeThreadPoolConfig类则实现了自定义线程池,具体实现如下:

package com.hugesoft.config.dto;
 
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
/**
 * 线程池配置属性类
 * @author YuXD
 */
@Data
@Component
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolConfig {
 
  /**
   * 核心线程数
   */
  private int corePoolSize;
 
  /**
   * 最大线程数
   */
  private int maxPoolSize;
 
  /**
   * 线程空闲时间
   */
  private int keepAliveSeconds;
 
  /**
   * 任务队列容量(阻塞队列)
   */
  private int queueCapacity;
}
package com.hugesoft.config;
 
import com.hugesoft.config.dto.TaskThreadPoolConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
 
/**
 * 重写默认线程池配置
 * @author YuXD
 */
@Slf4j
@Configuration
@EnableAsync
public class OverrideDefaultThreadPoolConfig implements AsyncConfigurer {
 
  @Autowired
  private TaskThreadPoolConfig config;
 
  @Override
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    //核心线程池大小
    executor.setCorePoolSize(config.getCorePoolSize());
    //最大线程数
    executor.setMaxPoolSize(config.getMaxPoolSize());
    //队列容量
    executor.setQueueCapacity(config.getQueueCapacity());
    //活跃时间
    executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
    //线程名字前缀
    executor.setThreadNamePrefix("default-thread-");
    /*
      当poolSize已达到maxPoolSize,如何处理新任务(是拒绝还是交由其它线程处理)
      CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
     */
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
  }
 
  /**
   * 异步任务中异常处理
   *
   * @return
   */
  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return (ex, method, params) -> {
      log.error("==========================" + ex.getMessage() + "=======================", ex);
      log.error("exception method:" + method.getName());
    };
  }
}
package com.hugesoft.config;
 
import com.hugesoft.config.dto.TaskThreadPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
 
/**
 *
 * 自定义下城
 * @author : YuXD
 */
@Configuration
@EnableAsync
public class CustomizeThreadPoolConfig {
 
  @Autowired
  private TaskThreadPoolConfig config;
 
  @Bean("customizeThreadPool")
  public Executor doConfigCustomizeThreadPool() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    //核心线程池大小
    executor.setCorePoolSize(config.getCorePoolSize());
    //最大线程数
    executor.setMaxPoolSize(config.getMaxPoolSize());
    //队列容量
    executor.setQueueCapacity(config.getQueueCapacity());
    //活跃时间
    executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
    //线程名字前缀
    executor.setThreadNamePrefix("customize-thread-");
     /*
      当poolSize已达到maxPoolSize,如何处理新任务(是拒绝还是交由其它线程处理)
      CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
     */
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
  }
 
}

四、com.hugesoft.service包下的内容是从真实项目环境中提取出来的,IStatusAnalyseService定义了设备状态分析基础Service,JJDeviceDataAnalyseManager,ZHUDeviceDataAnalyseManager,ZZDeviceDataAnalyseManager三个子类分别提供了默认实现,AbstractDeviceDataAnalyseManager提取了三个子类用到的公共方法,代码没难度,理解即可;需要尤其注意AbstractDeviceDataAnalyseManager的两个重载方法,分别采用默认线程池和自定义线程池的方式,注意使用的异同点,这点也就是默认线程池和自定义线程池适用上的唯一不同点。具体试下如下:

package com.hugesoft.service;
 
/**
 * 参数分析基础Service,所有需要进行参数分析的都需要实现该接口
 *
 * @author YuXD
 */
public interface IStatusAnalyseService {
 
  /**
   * 设备状态解析处理
   *
   * @param start 开始时间
   * @param end  截止时间
   */
  void doStatusAnalyseHandle(String start, String end);
 
  /**
   * 设备状态解析处理
   *
   * @param end 截止时间
   */
  void doStatusAnalyseHandle(String end);
 
  /**
   * 获取数据类别
   *
   * @return
   */
  String getDataType();
 
}
package com.hugesoft.service.impl;
 
 
import com.hugesoft.service.IStatusAnalyseService;
import org.springframework.scheduling.annotation.Async;
 
import java.util.Random;
 
/**
 * 抽象的设备数据分析Manager
 *
 * @author YuXD
 * @since 2020-06-18 22:47
 */
public abstract class AbstractDeviceDataAnalyseManager implements IStatusAnalyseService {
 
  @Async("customizeThreadPool")
  @Override
  public void doStatusAnalyseHandle(String start, String end) {
    int sleepSecOnds= new Random().nextInt(3) + 1;
    try {
      Thread.sleep(sleepSeconds * 1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(getDataType() + "在自定义线程" + Thread.currentThread().getName() + "执行了" + sleepSeconds + "秒");
  }
 
  @Async
  @Override
  public void doStatusAnalyseHandle(String end) {
    int sleepSecOnds= new Random().nextInt(3) + 1;
    try {
      Thread.sleep(sleepSeconds * 1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(getDataType() + "在默认线程" + Thread.currentThread().getName() + "执行了" + sleepSeconds + "秒");
  }
}
package com.hugesoft.service.impl;
 
import org.springframework.stereotype.Service;
 
/**
 * @description: 机加设备数据分析Service实现类
 * @author: YuXD
 * @create: 2021-03-15 20:17
 **/
@Service("JJ")
public class JJDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager {
 
  @Override
  public String getDataType() {
    return "机加";
  }
 
}
package com.hugesoft.service.impl;
 
import org.springframework.stereotype.Service;
 
/**
 * @description: 铸造设备数据分析Service实现类
 * @author: YuXD
 * @create: 2020-06-18 22:56
 **/
@Service("ZHU")
public class ZHUDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager {
 
  @Override
  public String getDataType() {
    return "铸造";
  }
 
}
package com.hugesoft.service.impl;
 
import org.springframework.stereotype.Service;
 
/**
 * @description: 总装设备数据分析Service实现类
 * @author: YuXD
 * @create: 2020-06-18 22:56
 **/
@Service("ZZ")
public class ZZDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager {
 
  @Override
  public String getDataType() {
    return "总装";
  }
 
}

五、最后看一下Springboot启动类实现;该类既是启动类也是Controller类,没什么特别要说明的。

package com.hugesoft;
 
import com.hugesoft.service.IStatusAnalyseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.List;
 
@RestController
@EnableAsync
@SpringBootApplication
public class SpringbootAsyncApplication {
 
  @Autowired
  private List statusAnalyseServiceList;
 
  public static void main(String[] args) {
    SpringApplication.run(SpringbootAsyncApplication.class, args);
  }
 
  @GetMapping("/sayHelloAsync")
  public String sayHelloAsync() {
    for (IStatusAnalyseService statusAnalyseService : statusAnalyseServiceList) {
      // 采用自定义线程池
      statusAnalyseService.doStatusAnalyseHandle(null, null);
      // 采用默认线程池
      statusAnalyseService.doStatusAnalyseHandle(null);
    }
    return "Hello, Async!";
  }
 
}

六、最后启动main方法,通过浏览器地址栏访问 http://localhost:8080/sayHelloAsync,发现秒出现如下页面,且控制台会出现如下内容,说明我们配置的默认线程池和自定义线程池都起作用了,到此,配置成功

到此这篇关于Springboot应用中线程池配置教程(2021版)的文章就介绍到这了,更多相关Springboot线程池配置内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 在更换电脑或重装系统时,用户经常需要处理诸如备份收藏夹、电子邮件、用户配置文件和常用文档等任务。尽管这些操作并不复杂,但却相当繁琐。本文介绍了一种利用Windows 7中的“轻松传送”功能,高效便捷地在两台电脑之间同步数据的方法,大大简化了这一过程。 ... [详细]
  • REST与RPC:选择哪种API架构风格?
    在探讨REST与RPC这两种API架构风格的选择时,本文首先介绍了RPC(远程过程调用)的概念。RPC允许客户端通过网络调用远程服务器上的函数或方法,从而实现分布式系统的功能调用。相比之下,REST(Representational State Transfer)则基于资源的交互模型,通过HTTP协议进行数据传输和操作。本文将详细分析两种架构风格的特点、适用场景及其优缺点,帮助开发者根据具体需求做出合适的选择。 ... [详细]
  • SSAS入门指南:基础知识与核心概念解析
    ### SSAS入门指南:基础知识与核心概念解析Analysis Services 是一种专为决策支持和商业智能(BI)解决方案设计的数据引擎。该引擎能够为报告和客户端应用提供高效的分析数据,并支持在多维数据模型中构建高性能的分析应用。通过其强大的数据处理能力和灵活的数据建模功能,Analysis Services 成为了现代 BI 系统的重要组成部分。 ... [详细]
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • JavaScript XML操作实用工具类:XmlUtilsJS技巧与应用 ... [详细]
  • 如何在WAMP环境中更改默认的www根目录路径
    在WAMP环境中更改默认的www根目录路径,可以通过编辑Apache配置文件实现。具体步骤如下:打开D:\WampServer\bin\apache\apache2.2.22\conf\httpd.conf文件,找到并修改DocumentRoot和指令,将路径更改为新的根目录位置。保存文件后重启WAMP服务,即可生效。此方法适用于需要自定义项目部署路径的开发者。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 本文详细介绍了如何安全地手动卸载Exchange Server 2003,以确保系统的稳定性和数据的完整性。根据微软官方支持文档(https://support.microsoft.com/kb833396/zh-cn),在进行卸载操作前,需要特别注意备份重要数据,并遵循一系列严格的步骤,以避免对现有网络环境造成不利影响。此外,文章还提供了详细的故障排除指南,帮助管理员在遇到问题时能够迅速解决,确保整个卸载过程顺利进行。 ... [详细]
  • 从文本中全面提取所有URL链接
    本文探讨了一种高效的方法,用于从大量文本数据中全面提取所有URL链接。通过使用正则表达式和Python编程语言,该方法能够准确识别并提取出各种格式的URL,包括HTTP、HTTPS等常见协议。实验结果表明,该方法在处理大规模文本数据时具有较高的准确率和效率。 ... [详细]
  • 在Eclipse中批量转换Java源代码文件的编码格式从GBK到UTF-8是一项常见的需求。通过编写简单的Java代码,可以高效地实现这一任务。该方法不仅适用于Java文件,还可以用于其他类型的文本文件编码转换。具体实现可以通过导入`java.io.File`类来操作文件系统,从而完成批量转换。此外,建议在转换过程中添加异常处理机制,以确保代码的健壮性和可靠性。 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
  • 本文深入解析了Spring Cloud路由网关Zuul的核心功能及其典型应用场景。通过对方志朋老师教材的学习和实践,详细探讨了Zuul在微服务架构中的重要作用,包括请求路由、过滤器链管理以及服务动态扩展等关键特性。同时,结合实际案例,展示了Zuul在高并发和复杂业务场景下的应用优势,为读者提供了全面的技术参考。 ... [详细]
  • 本文深入探讨了CGLIB BeanCopier在Bean对象复制中的应用及其优化技巧。相较于Spring的BeanUtils和Apache的BeanUtils,CGLIB BeanCopier在性能上具有显著优势。通过详细分析其内部机制和使用场景,本文提供了多种优化方法,帮助开发者在实际项目中更高效地利用这一工具。此外,文章还讨论了CGLIB BeanCopier在复杂对象结构和大规模数据处理中的表现,为读者提供了实用的参考和建议。 ... [详细]
  • 本文探讨了利用Java实现WebSocket实时消息推送技术的方法。与传统的轮询、长连接或短连接等方案相比,WebSocket提供了一种更为高效和低延迟的双向通信机制。通过建立持久连接,服务器能够主动向客户端推送数据,从而实现真正的实时消息传递。此外,本文还介绍了WebSocket在实际应用中的优势和应用场景,并提供了详细的实现步骤和技术细节。 ... [详细]
  • 深入浅出:PHP 中的 unset() 函数解析与应用
    本文深入解析了 PHP 中的 `unset()` 函数,详细介绍了其功能和应用场景。`unset()` 函数用于释放指定的变量,语法格式为 `void unset(mixed $var[, mixed $var[, ...]])`。通过具体示例,文章展示了如何在实际开发中有效使用 `unset()` 来管理内存和变量状态,帮助开发者提高代码效率和程序性能。 ... [详细]
author-avatar
张宏顺Lingling
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有