Spring定时任务并行(异步)处理方式

 更新时间:2024年08月02日 15:36:28   作者:BlueKitty1210  
这篇文章主要介绍了Spring定时任务并行(异步)处理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Spring定时任务并行(异步)

最近项目中遇到一个问题 , 在SpringBoot中设置了定时任务之后 , 在某个点总是没有执行 . 经过搜索研究发现 , spring 定时器任务scheduled-tasks默认配置是单线程串行执行的 .

即在当前时间点之内 . 如果同时有两个定时任务需要执行的时候 , 排在第二个的任务就必须等待第一个任务执行完毕执行才能正常运行.

如果第一个任务耗时较久的话 , 就会造成第二个任务不能及时执行 .

这样就可能由于时效性造成其他问题 . 而在实际项目中 , 我们也往往需要这些定时任务是"各干各的" , 而不是排队执行.

以下为默认串行的定时任务代码

package com.xbz.timerTask.task;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @title 测试spring定时任务执行
 * @createDate 2017年8月18日
 * @version 1.0
 */
@Component
@Configuration
@EnableScheduling
public class MyTestTask {
    private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Scheduled(fixedDelay = 1000)
    public void executeUpdateYqTask() {
        System.out.println(Thread.currentThread().getName() + " >>> task one " + format.format(new Date()));
    }

    @Scheduled(fixedDelay = 1000)
    public void executeRepaymentTask() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + " >>> task two " + format.format(new Date()));
        Thread.sleep(5000);
    }
}

启动项目之后 , 发现控制台输出如下 :

可以发现 , 一直是pool-5-thread-1一个线程在执行定时任务 , 这显然不符合我们的业务需求.

如何把定时任务改造成异步呢 , 在spring中网上文档较多 , 不再叙述 . 但在SpringBoot找到的相关资料也是新建xml文件的方式配置 , 实际上这就违背了SpringBoot减少配置文件的初衷 .

在SpringBoot可以自定义以下线程池配置

package com.xbz.config;

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer, AsyncConfigurer{

	/** 异步处理 */
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar){
		TaskScheduler taskScheduler = taskScheduler();
		taskRegistrar.setTaskScheduler(taskScheduler);
	}

	/** 定时任务多线程处理 */
	@Bean(destroyMethod = "shutdown")
	public ThreadPoolTaskScheduler taskScheduler(){
		ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
		scheduler.setPoolSize(20);
		scheduler.setThreadNamePrefix("task-");
		scheduler.setAwaitTerminationSeconds(60);
		scheduler.setWaitForTasksToCompleteOnShutdown(true);
		return scheduler;
	}

	/** 异步处理 */
	public Executor getAsyncExecutor(){
		Executor executor = taskScheduler();
		return executor;
	}

	/** 异步处理 异常 */
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
		return new SimpleAsyncUncaughtExceptionHandler();
	}
}

此时再启动定时任务 , 就发现已经是异步处理的了 .

如果项目中同时配置了异步任务的线程池和定时任务的异步线程处理

配置类如下 :

package com.xbz.config;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

/**
 * @title 使用自定义的线程池执行异步任务 , 并设置定时任务的异步处理 
 * @version 1.0
 */
@Configuration
@EnableAsync
@EnableScheduling
public class ExecutorConfig implements SchedulingConfigurer, AsyncConfigurer {

    private static final Logger LOG = LogManager.getLogger(ExecutorConfig.class.getName());

    @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("taskExecutor-");

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    /**
     * @title 异步任务中异常处理 
     * @description 
     * @author Xingbz
     * @createDate 2017年9月11日
     * @return
     * @see org.springframework.scheduling.annotation.AsyncConfigurer#getAsyncUncaughtExceptionHandler()
     * @version 1.0
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                LOG.error("==========================" + ex.getMessage() + "=======================", ex);
                LOG.error("exception method:" + method.getName());
            }
        };
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        TaskScheduler taskScheduler = taskScheduler();
        taskRegistrar.setTaskScheduler(taskScheduler);
    }
    
    /**
     * 并行任务使用策略:多线程处理
     * 
     * @return ThreadPoolTaskScheduler 线程池
     */
     @Bean(destroyMethod = "shutdown")
     public ThreadPoolTaskScheduler taskScheduler() {
         ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
         scheduler.setPoolSize(config.getCorePoolSize());
         scheduler.setThreadNamePrefix("task-");
         scheduler.setAwaitTerminationSeconds(60);
         scheduler.setWaitForTasksToCompleteOnShutdown(true);
         return scheduler;
     }
}

需要注意:

  • 这两个配置类只能同时配置一个 
  • 如果配置了第二个 , 则第一个就无需再用

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Mac M1 Java 开发环境配置详解

    Mac M1 Java 开发环境配置详解

    这篇文章主要介绍了Mac M1 Java 开发环境配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java中前台往后台传递多个id参数的实例

    Java中前台往后台传递多个id参数的实例

    下面小编就为大家带来一篇Java中前台往后台传递多个id参数的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Java实现的串口通信功能示例

    Java实现的串口通信功能示例

    这篇文章主要介绍了Java实现的串口通信功能,结合实例形式分析了java串口通信的具体操作步骤与相关注意事项,需要的朋友可以参考下
    2018-01-01
  • Java实现全图背景水印的示例详解

    Java实现全图背景水印的示例详解

    这篇文章主要为大家详细介绍了如何利用Java实现全图背景水印的方法,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-02-02
  • SpringCloud-Alibaba-Sentinel-配置持久化策略详解

    SpringCloud-Alibaba-Sentinel-配置持久化策略详解

    这篇文章主要介绍了SpringCloud-Alibaba-Sentinel-配置持久化策略,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Java中List与Map初始化的一些写法分享

    Java中List与Map初始化的一些写法分享

    这篇文章主要介绍了Java中List与Map初始化的一些写法有需要的朋友可以参考一下
    2014-01-01
  • springboot中.yml文件参数的读取方式

    springboot中.yml文件参数的读取方式

    这篇文章主要介绍了springboot中.yml文件参数的读取方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • 浅谈Java锁的膨胀过程以及一致性哈希对锁膨胀的影响

    浅谈Java锁的膨胀过程以及一致性哈希对锁膨胀的影响

    本文主要介绍了Java锁的膨胀过程以及一致性哈希对锁膨胀的影响,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Java开发到底为什么要用 IoC 和 AOP

    Java开发到底为什么要用 IoC 和 AOP

    这篇文章主要介绍了Java开发到底为什么要用 IoC 和 AOP,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-02-02
  • JDK源码白话解读之ThreadLocal篇

    JDK源码白话解读之ThreadLocal篇

    其实网上有很多关于ThreadLocal的文章了,有不少文章也已经写的非常好了。但是很多同学反应还有一些部分没有讲解的十分清楚,还是有一定的疑惑没有想的十分清楚
    2022-02-02

最新评论