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 并发编程的可见性、有序性和原子性

    Java 并发编程的可见性、有序性和原子性

    这篇文章主要介绍了Java 并发编程的可见性、有序性和原子性的相关资料,帮助大家更好的理解和学习Java并发编程,感兴趣的朋友可以了解下。
    2020-11-11
  • Arrays.sort(arr)是什么排序及代码逻辑

    Arrays.sort(arr)是什么排序及代码逻辑

    在学习过程中观察到Arrays.sort(arr)算法可以直接进行排序,但不清楚底层的代码逻辑是什么样子,今天通过本文给大家介绍下Arrays.sort(arr)是什么排序,感兴趣的朋友一起看看吧
    2022-02-02
  • Java数组高级算法与Arrays类常见操作小结【排序、查找】

    Java数组高级算法与Arrays类常见操作小结【排序、查找】

    这篇文章主要介绍了Java数组高级算法与Arrays类常见操作,结合实例形式总结分析了Java数组常见的排序算法、查找算法相关原理、实现与使用技巧,需要的朋友可以参考下
    2019-03-03
  • java多线程之线程安全的单例模式

    java多线程之线程安全的单例模式

    这篇文章主要为大家详细介绍了java多线程之线程安全的单例模式,文章内容全面,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • Java多线程实现Callable接口

    Java多线程实现Callable接口

    本文给大家分享的是使用Java多线程来实现callable接口的方法,以及使用方法,另外还有一个网友的实例,希望能够对大家掌握Java多线程有所帮助。
    2016-06-06
  • @Autowired注入为null的原因与解决方法

    @Autowired注入为null的原因与解决方法

    我们经常会通过@Autowired注解将某个类注到另一个类中,但是会发现注不进去,报NULL,所以本文就给大家分析了@Autowired 注入为null 的原因与解决方法,需要的朋友可以参考下
    2023-09-09
  • Springboot+Hutool自定义注解实现数据脱敏

    Springboot+Hutool自定义注解实现数据脱敏

    我们在项目中会处理敏感数据时,通常需要对这些数据进行脱敏,本文主要使用了Springboot整合Hutool来自定义注解实现数据脱敏,感兴趣的可以理解下
    2023-10-10
  • idea指定maven的settings文件不生效的问题解决

    idea指定maven的settings文件不生效的问题解决

    本文主要介绍了idea指定maven的settings文件不生效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Spring MVC请求处理流程和九大组件详解

    Spring MVC请求处理流程和九大组件详解

    这篇文章主要介绍了Spring MVC请求处理流程和九大组件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-12-12
  • 详解Java中@Override的作用

    详解Java中@Override的作用

    这篇文章主要介绍了详解Java中@Override的作用的相关资料,希望通过本文能帮助到大家,让大家理解这部分内容,需要的朋友可以参考下
    2017-10-10

最新评论