Spring Boot 配置线程池详解,并使用@Async,执行异步方法

Spring Boot 配置线程池详解 线程池是多线程的处理机制,线程池一般用于需要大量线程完成任务,并且完成时间较短时使用,大量用于并发框架和异步执行任务。

一、配置线程池的优势 降低资源消耗,通过利用已创建的线程降低线程创建和销毁造成的消耗 有利于线程的可控性,如果线程无休止创建,会导致内存耗尽。 提高系统响应速度,通过使用已存在的线程,不需要等待新线程的创建就可以立即执行当前任务。 二、spring中线程池ThreadPoolTaskExecutor的主要参数说明。

vbnet 复制代码
corePoolSize:核心线程数,默认的核心线程的1,向线程池提交一个任务时,如果线程池已经创建的线程数小于核心线程数,即使此时存在空闲线程,也会通过创建一个新线程来执行新任务,知道创建的线程等于核心线程数时,如果有空闲线程,则使用空闲线程。
maxPoolSize:最大线程数,默认的最大线程数为Integer.MAX_VALUE 即231-1。当队列满了之后
keepAliveSeconds:允许线程空闲时间,默认的线程空闲时间为60秒,当线程中的线程数大于核心线程数时,线程的空闲时间如果超过线程的存活时间,则此线程会被销毁,直到线程池中的线程数小于等于核心线程数时。
queueCapacity:缓冲队列数,默认的缓冲队列数是Integer.MAX_VALUE 即231-1,用于保存执行任务的阻塞队列
allowCoreThreadTimeOut:销毁机制,allowCoreThreadTimeOut为true则线程池数量最后销毁到0个。allowCoreThreadTimeOut为false销毁机制:超过核心线程数时,而且(超过最大值或者timeout过),就会销毁。默认是false

三、Spring Boot 中创建线程池ThreadPoolTaskExecutor

php 复制代码
/**
 * @author yly
 * @ClassName ThreadPoolConfig
 * @Date 2020/2/26 12:10
 * @Version 1.0
 **/
@Configuration
public class ThreadPoolConfig {

        /**
     * 核心线程数
     * 默认的核心线程数为1
     *
     */
    private static final int CORE_POOL_SIZE = 5;
    /**
     * 最大线程数
     * 默认的最大线程数是Integer.MAX_VALUE 即2<sup>31</sup>-1
     */
    private static final int MAX_POOL_SIZE = 50;
    /**
     * 缓冲队列数
     * 默认的缓冲队列数是Integer.MAX_VALUE 即2<sup>31</sup>-1
     */
    private static final int QUEUE_CAPACITY = 100;

    /**
     * 允许线程空闲时间
     * 默认的线程空闲时间为60秒
     */
    private static final int KEEP_ALIVE_SECONDS = 30;

    /**
     * 线程池前缀名
     */
    private static final String THREAD_NAME_PREFIX = "Task_Service_Async_";

    /**
     * allowCoreThreadTimeOut为true则线程池数量最后销毁到0个
     * allowCoreThreadTimeOut为false
     * 销毁机制:超过核心线程数时,而且(超过最大值或者timeout过),就会销毁。
     * 默认是false
     */
    private boolean allowCoreThreadTimeOut = false;

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {

        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
        taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
        taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        taskExecutor.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //线程池初始化
        taskExecutor.initialize();
        return taskExecutor;
    }
 }

其中setRejectedExecutionHandler()设置拒绝执行处理程序。

less 复制代码
/**
 * Set the RejectedExecutionHandler to use for the ExecutorService.
 * Default is the ExecutorService's default abort policy.
 * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
 */
public void setRejectedExecutionHandler(@Nullable RejectedExecutionHandler rejectedExecutionHandler) {
	this.rejectedExecutionHandler =
			(rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy());
}

ThreadPoolTaskExecutor 的拒绝执行处理程序共有四种,在ThreadPoolExecutor类中

第一种处理程序CallerRunsPolicy

php 复制代码
  /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
    public static class CallerRunsPolicy implements RejectedExecutionHandler {

被拒绝任务的处理程序,它直接在{@code execute}方法的调用线程中运行被拒绝的任务,除非执行程序已经关闭,在这种情况下任务被丢弃。

第二种处理程序AbortPolicy

php 复制代码
  /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
public static class AbortPolicy implements RejectedExecutionHandler {

抛出{@code RejectedExecutionException}的被拒绝任务的处理程序。

第三种处理程序DiscardOldestPolicy

php 复制代码
  /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {

被拒绝任务的处理程序,它丢弃最古老的未处理请求,然后重试{@code execute},除非执行程序关闭,在这种情况下任务被丢弃。

第四种处理程序DiscardPolicy

php 复制代码
  /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    public static class DiscardPolicy implements RejectedExecutionHandler {

一个处理被拒绝任务的处理程序,它无声地丢弃被拒绝的任务。

四、Spring Boot 中创建线程池ThreadPoolTaskScheduler

java 复制代码
/**
 * @author yly
 * @ClassName ThreadPoolConfig
 * @Date 2020/2/26 12:10
 * @Version 1.0
 **/
@Configuration
public class ThreadPoolConfig {

    /**
     * 设置调度的执行程序服务的池大小。
     */
    private int poolSize = 4;
    /**
     * 设置任务注册器的调度器
     * 在{@link ScheduledThreadPoolExecutor}上设置取消操作模式。默认是{@code false}。
     * 如果设置为{@code true},则目标执行程序将切换到"删除-取消"模式(如果可能,则使用软回退)。
     * 这个设置可以在运行时修改,例如通过JMX。
     *
     */
    private boolean removeOnCancelPolicy = true;
    /**
     * 线程池前缀名
     */
    private String threadNamePrefix = "TaskSchedulerThreadPool-";

    @Bean("taskScheduler")
    public ThreadPoolTaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();

        scheduler.setPoolSize(poolSize);

        scheduler.setRemoveOnCancelPolicy(removeOnCancelPolicy);

        scheduler.setThreadNamePrefix(threadNamePrefix);
        return scheduler;
    }
}

ThreadPoolTaskScheduler参数说明

less 复制代码
poolSize :设置调度的执行程序服务的池大小。
removeOnCancelPolicy : * 设置任务注册器的调度器在{@link ScheduledThreadPoolExecutor}上设置取消操作模式。默认是{@code false}。如果设置为{@code true},则目标执行程序将切换到"删除-取消"模式(如果可能,则使用软回退)。这个设置可以在运行时修改。
threadNamePrefix :线程池前缀名
其中setErrorHandler()方法是自定义错误处理程序策略

五、 线程池的创建流程 创建线程池时,首先判断线程池的核心线程池是否已满,没满则创建新的工作线程执行任务,否则判断任务队列是否已满,没满则将新提交的任务添加到工作队列,否则判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,否执行饱和策略。

六、线程池参数推荐设置

scss 复制代码
corePoolSize:核心线程数(taskstasktime个线程数),假设系统每秒任务数为100到1000,每个任务耗时0.1秒,则需要1000.1至1000*0.1,即10到100个线程
maxPoolSize:最大线程数,每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200),即60个线程,可将maxPoolSize设置为60。
queueCapacity:缓冲队列数,队列长度可以设置为(corePoolSize/tasktime)responsetime: (20/0.1)2=400,即队列长度可设置为400。*

七、使用@Async,异步方法执行 (1)创建SpringBoot web项目

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yly</groupId>
    <artifactId>async</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>async</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

(2)在启动类 上添加@EnableAsync和@EnableScheduling(为了查看使用线程池配置定时任务)

less 复制代码
/**
 * @author 81509
 */
@SpringBootApplication
@EnableAsync
@EnableScheduling
public class AsyncApplication {

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

}

(3)创建SendSmsService

typescript 复制代码
/**
 * @author yly
 * @ClassName SendSmsService
 * @Date 2020/2/26 11:31
 * @Version 1.0
 **/
@Service
@Async("taskExecutor")//可以加在类上或方法上
public class SendSmsService {

    Logger logger = LoggerFactory.getLogger(getClass());

    public void sendMerchant() {
        logger.info("通知商铺--------开始");
        try {
            logger.info("sleep----3秒");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("通知商铺--------完成");
    }

    public void sendConsumer()  {
        logger.info("通知消费者---------开始");
        try {
            logger.info("sleep----1秒");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("通知消费者---------完成");
    }
}

(4)配置线程池

php 复制代码
/**
 * @author yly
 * @ClassName ThreadPoolConfig
 * @Date 2020/2/26 12:10
 * @Version 1.0
 **/
@Configuration
public class ThreadPoolConfig {

        /**
     * 核心线程数
     * 默认的核心线程数为1
     *
     */
    private static final int CORE_POOL_SIZE = 5;
    /**
     * 最大线程数
     * 默认的最大线程数是Integer.MAX_VALUE 即2<sup>31</sup>-1
     */
    private static final int MAX_POOL_SIZE = 50;
    /**
     * 缓冲队列数
     * 默认的缓冲队列数是Integer.MAX_VALUE 即2<sup>31</sup>-1
     */
    private static final int QUEUE_CAPACITY = 100;

    /**
     * 允许线程空闲时间
     * 默认的线程空闲时间为60秒
     */
    private static final int KEEP_ALIVE_SECONDS = 30;

    /**
     * 线程池前缀名
     */
    private static final String THREAD_NAME_PREFIX = "Task_Service_Async_";

    /**
     * allowCoreThreadTimeOut为true则线程池数量最后销毁到0个
     * allowCoreThreadTimeOut为false
     * 销毁机制:超过核心线程数时,而且(超过最大值或者timeout过),就会销毁。
     * 默认是false
     */
    private boolean allowCoreThreadTimeOut = false;

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {

        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
        taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
        taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        taskExecutor.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }
}

(5)创建demo类调用异步方法,需要加上@Component加入到容器中

java 复制代码
/**
 * @author yly
 * @ClassName demo
 * @Date 2020/2/26 11:37
 * @Version 1.0
 **/
@Component
public class demo {

    @Autowired
    private SendSmsService sendSmsService;

    @Scheduled(fixedRate  = 1000)//定时任务,一秒执行一次
    public void test()  {
        sendSmsService.sendMerchant();
        sendSmsService.sendConsumer();
    }
}

(6)执行结果

------------------------------------------------ 版权声明:本文为CSDN博主「CodeRemote」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/qq_43813937...

相关推荐
努力的小郑6 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
Victor3567 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3567 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁8 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp8 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
宁瑶琴9 小时前
COBOL语言的云计算
开发语言·后端·golang
普通网友9 小时前
阿里云国际版服务器,真的是学生党的性价比之选吗?
后端·python·阿里云·flask·云计算
IT_陈寒10 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
Soofjan11 小时前
Go 内存回收-GC 源码1-触发与阶段
后端
shining11 小时前
[Golang]Eino探索之旅-初窥门径
后端