SpringBoot中使用异步调度程序的高级方法

 更新时间:2024年07月04日 09:45:24   作者:Eddie_920  
本文主要介绍了SpringBoot中使用异步调度程序的高级方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

案例:

有两个调度器,一个调度器每3分钟运行一次从数据库中获取数据,另一个调度器每分钟运行一次调用REST API。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
public class YourSpringBootApplication {

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

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class FirstScheduler {

    @Scheduled(fixedRate = 3 * 60 * 1000) // 每3分钟运行一次
    public void getDataFromDB() {
        // 从数据库获取数据的代码
    }
}

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SecondScheduler {

    @Scheduled(fixedRate = 1 * 60 * 1000) // 每1分钟运行一次
    public void consumeRestApi() {
        // 调用REST API的代码
    }
}

问题:

当第一个调度器运行时间超过预期时间,例如执行了10分钟,那么第二个调度器则不会运行,一直到第一个调度器运行结束后。

上述代码存在的问题,当一个调度器运行时,第二个调度器会被阻塞。

解决方案:

为了避免第二个调度器因第一个调度器而被阻塞,可以使用Spring的异步执行。这样做每个调度器都将在自己单独线程中运行从而实现独立工作。

实现代码:

在使用Spring的异步执行支持时,最佳做法是配置一个自定义线程池来控制异步任务执行所用的线程数量。默认情况下Spring使用SimpleAsyncTaskExecutor(这在生产环境中可能不太合适,因为它不提供更多对线程池的控制方法。)
为了解决这个问题建议在应用程序中创建一个自定义线程池bean。以下是实现代码:

步骤1:定义一个配置类创建自定义线程池bean。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfiguration {

    @Bean(name = "asyncTaskExecutor")
    public ThreadPoolTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 设置线程池中的初始线程数
        executor.setMaxPoolSize(10); // 设置线程池中的最大线程数
        executor.setQueueCapacity(25); // 设置用于保存挂起任务的队列容量
        executor.setThreadNamePrefix("AsyncTask-"); // 设置线程名前缀
        executor.initialize();
        return executor;
    }
}

步骤2:修改第一个调度器,使其使用此自定义线程池。

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class FirstScheduler {

    @Async("asyncTaskExecutor") // 指定自定义线程池bean名称
    @Scheduled(fixedRate = 3 * 60 * 1000) // 每3分钟运行一次
    public void getDataFromDB() {
        // 从数据库获取数据的代码
        // 此方法将在自定义线程池中异步运行
    }
}

通过这种配置,第一个调度器方法(getDataFromDB)将在自定义线程池中异步运行,而第二个调度器方法(consumeRestApi)则在默认调度器的线程中运行。

根据应用程序需求和可用系统资源调整corePoolSize、maxPoolSize和queueCapacity值。线程池配置对应用程序的性能产生比较重要的影响,因此需要适当调整这些值。

要使第二个调度器也使用自定义线程池进行异步执行,需要在consumeRestApi方法的@Async注解中添加taskExecutor属性。这样确保两个调度器都在相同的自定义线程池中异步运行。以下是更新后的代码:

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SecondScheduler {

    @Async("asyncTaskExecutor") // 指定自定义线程池bean名称
    @Scheduled(fixedRate = 1 * 60 * 1000) // 每1分钟运行一次
    public void consumeRestApi() {
        // 调用REST API的代码
        // 此方法将在自定义线程池中异步运行
    }
}

通过这种设置,第一个调度器(getDataFromDB)和第二个调度器(consumeRestApi)都将在相同的自定义线程池中异步运行。这将允许它们独立地工作,即使其中一个任务需要更长时间来完成。

使用自定义线程池
记录错误消息,当所需线程池大小 > 配置的线程池大小时:
要在所需线程池大小超过配置的线程池大小时记录错误消息,可以利用Spring的ThreadPoolTaskExecutor的RejectedExecutionHandler。当线程池的任务队列已满且线程池无法接受更多任务时,将调用此处理程序。可以使用此回调来记录错误消息。

以下是更新的配置类,其中包含RejectedExecutionHandler:

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.ThreadPoolExecutor;

@Configuration
@EnableAsync
@Sl4J
public class AsyncConfiguration {

    @Bean(name = "asyncTaskExecutor")
    public ThreadPoolTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 设置线程池中的初始线程数
        executor.setMaxPoolSize(10); // 设置线程池中的最大线程数
        executor.setQueueCapacity(25); // 设置用于保存挂起任务的队列容量
        executor.setThreadNamePrefix("AsyncTask-"); // 设置线程名前缀

        // 设置RejectedExecutionHandler以记录错误消息
        executor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor e) -> {
            // 记录错误消息,队列已满,任务被拒绝
            // 根据需要自定义此消息
            log.error("任务被拒绝:线程池已满。请增加线程池大小。");
        });

        executor.initialize();
        return executor;
    }
}

通过这种配置,当线程池的队列已满且尝试提交额外任务时,RejectedExecutionHandler会被触发。
你可以根据应用程序的需求自定义错误消息或采取其他操作。

请注意,设置正确的线程池大小和队列容量对于应用程序的性能和资源利用率至关重要。

如果因为队列容量不足而任务持续被拒绝则需要增加线程池大小或调整队列容量。

总结:

通过配置共享的自定义线程池、利用@Async注解以及整合RejectedExecutionHandler,可以使应用程序有效地管理和执行多个调度器并发从而确保调度器独立运行,并且系统能够优雅地响应线程池限制。

到此这篇关于SpringBoot中使用异步调度程序的高级方法的文章就介绍到这了,更多相关SpringBoot 异步调度内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现读取Excel文件功能(EasyExcel初使用)

    Java实现读取Excel文件功能(EasyExcel初使用)

    EasyExcel是一款基于Java语言的开源Excel解析工具,可以帮助我们快速、高效地读取和写入Excel文件,这篇文章主要给大家介绍了关于Java实现读取Excel文件功能的相关资料,使用的是EasyExcel,需要的朋友可以参考下
    2024-07-07
  • Spring Boot配置动态更新问题

    Spring Boot配置动态更新问题

    这篇文章主要介绍了Spring Boot配置动态更新问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • SpringBoot使用DevTools实现后端热部署的过程详解

    SpringBoot使用DevTools实现后端热部署的过程详解

    在Spring Boot项目中,Spring Boot官方提供你了Devtools热部署模块,通过maven的方式导入就能使用,本文主要SpringBoot通过DevTools实现热部署,感兴趣的朋友一起看看吧
    2023-11-11
  • 图解Java经典算法插入排序的原理与实现

    图解Java经典算法插入排序的原理与实现

    插入排序的算法描述是一种简单直观的排序算法。其原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。本文将用Java语言实现插入排序算法并进行可视化,感兴趣的可以了解一下
    2022-09-09
  • 关于postman传参的几种格式 list,map 等

    关于postman传参的几种格式 list,map 等

    这篇文章主要介绍了postman传参的几种格式 list,map等,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Spring mvc如何实现数据处理

    Spring mvc如何实现数据处理

    这篇文章主要介绍了Spring mvc如何实现数据处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Java中减少if-else的设计模式和优化技巧

    Java中减少if-else的设计模式和优化技巧

    “过于依赖 if-else 不仅会让代码变得臃肿不堪,还会使维护成本大大增加,其实,if-else 虽然是最基础的条件分支,但它并不是万能的解决方案,今天,我将带大家了解一些取代 if-else 的设计模式和优化技巧,需要的朋友可以参考下
    2025-04-04
  • Java 多线程与并发编程全面实战指南

    Java 多线程与并发编程全面实战指南

    本文系统讲解Java多线程与并发编程,涵盖线程创建、同步机制(synchronized/Lock)、通信工具、并发集合、线程池及CompletableFuture等现代技术,强调正确使用多线程的重要性,提供避免死锁、内存可见性等常见问题的实践指南,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • mybatisPlus中批量删除的示例代码

    mybatisPlus中批量删除的示例代码

    本文主要介绍了mybatisPlus中批量删除的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Intellij IDEA 2018配置Java运行环境的方法步骤

    Intellij IDEA 2018配置Java运行环境的方法步骤

    这篇文章主要介绍了Intellij IDEA 2018配置Java运行环境的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09

最新评论