SpringBoot配置线程池的实现示例

 更新时间:2023年09月22日 15:48:47   作者:何不语i  
本文主要介绍了SpringBoot配置线程池的实现示例,主要包括在Spring Boot中创建和配置线程池,包括设置线程池的大小、队列容量、线程名称等参数,感兴趣的可以了解一下

一.线程池简介

1.1.什么是线程池

线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。在JAVA中主要是使用ThreadPoolExecutor类来创建线程池,并且JDK中也提供了Executors工厂类来创建线程池(不推荐使用)。

1.2.线程池的优点

降低资源消耗,复用已创建的线程来降低创建和销毁线程的消耗。提高响应速度,任务到达时,可以不需要等待线程的创建立即执行。提高线程的可管理性,使用线程池能够统一的分配、调优和监控。

1.3.线程池五种状态

线程池ThreadPoolExecutor分别有五种状态分别为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED使用一个AtomicInteger类型的ctl字段来描述线程池地运行状态和线程数量,通过ctl的高三位来表示线程池的5种状态,低29位表示线程池中现有的线程数量。

 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    /**
     * 线程池线程数地bit数
     */
    private static final int COUNT_BITS = Integer.SIZE - 3;
    /**
     * 线程池中最大线程容量
     */
    private static final int CAPACITY = (1 << COUNT_BITS) - 1;
    /**
     * 表示可接受新任务,且可执行队列中的任务;
     */
    private static final int RUNNING = -1 << COUNT_BITS;
    /**
     * 表示不接受新任务,但可执行队列中的任务;
     */
    private static final int SHUTDOWN = 0 << COUNT_BITS;
    /**
     * 表示不接受新任务,且不再执行队列中的任务,且中断正在执行的任务;
     */
    private static final int STOP = 1 << COUNT_BITS;
    /**
     * 所有任务已经中止,且工作线程数量为0,最后变迁到这个状态的线程将
     * 要执行terminated()钩子方法,只会有一个线程执行这个方法;
     */
    private static final int TIDYING = 2 << COUNT_BITS;
    /**
     * TERMINATED,中止状态,已经执行完terminated()钩子方法;
     */
    private static final int TERMINATED = 3 << COUNT_BITS;

线程池状态之间的流转图如下所示

1.4.线程池处理流程

线程池处理流程代码如下所示

    /**
     * 在将来某个时候执行给定的任务。该任务可以在新线程中执行,也可以在现有的池线程中执行。如果由于此执行
     * 器已关闭或已达到其容量,任务无法提交执行,则由当前RejectedExecutionHandler处理该任务。
     * Params:
     * 命令–要执行的任务
     * Throws:
     * RejectedExecutionException–如果任务无法接受执行,则由RejectedExecutionHandler自行决定
     * NullPointerException–如果命令为null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         分三步走:
         1.如果运行的线程少于corePoolSize,请尝试以给定的命令作为第一个任务来启动一个新线程。
         对addWorker的调用原子地检查runState和workerCount,因此,通过返回false,可以防止
         在不应该添加线程的情况下添加线程的错误警报。
         2.如果一个任务可以成功排队,那么我们仍然需要仔细检查我们是否应该添加一个线程(因为自上
         次检查以来已有的线程已经失效),或者池是否在进入该方法后关闭。因此,如果有必要,我们重
         新检查状态,如果停止,则回滚排队,如果workerCount为0,则启动一个新线程。
         3.如果我们不能对任务进行排队,那么我们尝试添加一个新线程。如果它失败了,我们知道线程池
         被关闭或饱和了,所以拒绝了这项任务。
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (!isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        } else if (!addWorker(command, false))
            reject(command);
    }

线程池处理流程图如下所示

二.开始配置

ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。

2.1.依赖导入

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

2.2.配置application.properties文件

universe.thread.pool.executor.threadNamePrefix=threadPoolTaskExecutor-
universe.thread.pool.executor.queueCapacity=100
universe.thread.pool.executor.rejectedExecutionHandler=java.util.concurrent.ThreadPoolExecutor$AbortPolicy
universe.thread.pool.executor.keepAliveSeconds=60

2.3.创建PoolExecutorConfig.java

package com.temperature.humidity.system.config.thread;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Data
@Log4j2
public class PoolExecutorConfig {
    /**
     * 获取CPU核数
     */
    private static final int CPU_NUM;
    /**
     * 配置线程池的前缀
     */
    @Value("${universe.thread.pool.executor.threadNamePrefix}")
    private String threadNamePrefix;
    /**
     * 线程池的核心线程数。在没有设置 allowCoreThreadTimeOut 为 true 的情况下,
     * 核心线程会在线程池中一直存活,即使处于闲置状态。
     */
    private Integer corePoolSize;
    /**
     * 线程池中的任务队列,通过线程池的 execute() 方法提交的 Runnable
     * 对象会存储在该队列中。
     */
    @Value("${universe.thread.pool.executor.queueCapacity}")
    private Integer queueCapacity;
    /**
     * 线程池所能容纳的最大线程数。当活动线程(核心线程+非核心线程)达到这个数值后,
     * 后续任务将会根据 RejectedExecutionHandler 来进行拒绝策略处理。
     */
    private Integer maxPoolSize;
    /**
     * 当任务无法被执行时(超过线程最大容量 maximum 并且 workQueue 已经被排满了)的处理策略,
     * - AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
     * - DiscardPolicy:丢弃任务,但是不抛出异常。
     * - DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
     * - CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
     */
    @Value("${universe.thread.pool.executor.rejectedExecutionHandler}")
    private String rejectedExecutionHandler;
    /**
     * 非核心线程 闲置时的超时时长。超过该时长,非核心线程就会被回收。若线程池通设置
     * 核心线程也允许 timeOut,即 allowCoreThreadTimeOut 为 true,则该时长
     * 同样会作用于核心线程,在超过aliveTime 时,核心线程也会被回收,AsyncTask
     * 配置的线程池就是这样设置的。
     */
    @Value("${universe.thread.pool.executor.keepAliveSeconds}")
    private Integer keepAliveSeconds;
    public PoolExecutorConfig() {
        log.info("该台服务器的CPU核心数为{}", CPU_NUM);
        this.corePoolSize = CPU_NUM + 1;
        this.maxPoolSize = 2 * CPU_NUM;
    }
    static {
        CPU_NUM = Runtime.getRuntime().availableProcessors();
    }
}

2.4.创建OurPoolExecutor.java

package com.temperature.humidity.system.config.thread;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
/**
 * @description 线程池配置类
 */
@Configuration
@Log4j2
public class OurPoolExecutor {
    @Autowired
    private PoolExecutorConfig threadPoolExecutorConfig;
    @Bean
    public Executor threadPoolTaskExecutor() {
        String threadNamePrefix = threadPoolExecutorConfig.getThreadNamePrefix();
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        try {
            log.info("开始初始化线程池 -->threadPoolTaskExecutor");
            taskExecutor.setThreadNamePrefix(threadNamePrefix);
            taskExecutor.setCorePoolSize(threadPoolExecutorConfig.getCorePoolSize());
            taskExecutor.setMaxPoolSize(threadPoolExecutorConfig.getMaxPoolSize());
            taskExecutor.setQueueCapacity(threadPoolExecutorConfig.getQueueCapacity());
            //通过反射获取RejectedExecutionHandlerClass 的类模板
            Class<?> rejectedExecutionHandlerClass = Class.forName(threadPoolExecutorConfig.getRejectedExecutionHandler());
            //获取RejectedExecutionHandlerClass类的实例
            RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) rejectedExecutionHandlerClass.newInstance();
            taskExecutor.setRejectedExecutionHandler(rejectedExecutionHandler);
            taskExecutor.setKeepAliveSeconds(threadPoolExecutorConfig.getKeepAliveSeconds());
            //进行加载
            taskExecutor.initialize();
            log.info("初始化线程池完成:{}核心线程为{}-->", threadNamePrefix, taskExecutor.getCorePoolSize());
            return taskExecutor;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("初始化线程池失败:{}失败原因为:{}", threadNamePrefix, e.getMessage());
            return null;
        }
    }
}

三.测试

我们配置线程池后启动我们的SpringBoot项目,可以看到我们的控制台打印出来了如下图中圈中的日志,测试成功。

到此这篇关于SpringBoot配置线程池的实现示例的文章就介绍到这了,更多相关SpringBoot 线程池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 如何在Spring Boot中实现异步处理与并发控制

    如何在Spring Boot中实现异步处理与并发控制

    本文我们将深入探讨如何在Spring Boot中实现异步处理与并发控制,这一过程涉及到异步任务的执行、线程池的配置、以及并发控制的实践,以帮助我们提升应用的性能和响应能力,感兴趣的朋友跟随小编一起看看吧
    2024-07-07
  • Java中MapStruct入门使用及对比

    Java中MapStruct入门使用及对比

    MapStruct是一个Java注解处理器框架,用于简化Java Bean之间的映射,本文主要介绍了Java中MapStruct入门使用及对比,感兴趣的可以了解一下
    2023-12-12
  • Spring MVC Annotation验证的方法

    Spring MVC Annotation验证的方法

    这篇文章主要介绍了Spring MVC Annotation验证的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • java面向对象编程类的内聚性分析

    java面向对象编程类的内聚性分析

    高内聚、低耦合是软件设计中非常关键的概念。在面向对象程序设计中类的划分时,类的内聚性越高,其封装性越好,越容易复用
    2021-10-10
  • springboot结合JWT实现单点登录的示例

    springboot结合JWT实现单点登录的示例

    本文主要介绍了springboot结合JWT实现单点登录的示例,包括生成Token、验证Token及使用Redis存储Token,具有一定的参考价值,感兴趣的可以了解一下
    2025-01-01
  • Java开发实现飞机大战

    Java开发实现飞机大战

    这篇文章主要为大家详细介绍了Java开发实现飞机大战,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Java 精炼解读方法的定义与使用

    Java 精炼解读方法的定义与使用

    Java语言中的“方法”(Method)在其他语言当中也可能被称为“函数”(Function)。对于一些复杂的代码逻辑,如果希望重复使用这些代码,并且做到“随时任意使用”,那么就可以将这些代码放在一个大括号“{}”当中,并且起一个名字。使用的时候,直接找到名字调用即可
    2022-03-03
  • java String校招面试题过程详解

    java String校招面试题过程详解

    这篇文章主要介绍了java String校招面试题过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • 通过prometheus监控springboot程序运行状态的操作流程

    通过prometheus监控springboot程序运行状态的操作流程

    jmx_exporter用于从Java应用程序中提取JMX指标,适用于SpringBoot应用,通过下载jar包和配置文件,可以抓取JVM基础指标,要获取应用级别指标,需要集成Prometheus客户端库并自定义指标,本文给大家介绍了如何通过prometheus监控springboot程序运行状态
    2025-02-02
  • SpringBoot如何实现接口版本控制

    SpringBoot如何实现接口版本控制

    这篇文章主要介绍了SpringBoot如何实现接口版本控制,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10

最新评论