SpringBoot项目多线程实现定时任务-只需要三步

发布于:2024-08-24 ⋅ 阅读:(105) ⋅ 点赞:(0)

众所周知,项目中需要使用定时任务发布的需求时非常常见的,例如:数据同步,清理垃圾文件,清理过期用户等需求,可能需要我们定时去清理数据。

但是我们如果集成xxl-job,Quartz,spring task等定时任务框架,但是我们如果只是针对某些小需求进行定时任务,完全用不到这样调度框架,Spring Boot框架中集成了@Schedule定时任务。

在我们使用@Schedule注解的时候,需要注意的时,这是一个单线程的定时任务,也就是说当我们在同一时间进行执行多个任务的时候,可能会出现第二个任务无法执行(不是绝对的)、任务执行顺序也不一样(有可能第一个任务先执行,也有可能是第二个任务先执行)。

所以在这里我们要想到使用多线程的方式。

第一步:pom文件修改

在我们创建完成springboot项目后,其实pom文件无需导入其他的依赖了,因为spring-boot-starter这个依赖中了。

第二步:配置ScheduleConfig

package com.example.demo.config;

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

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableScheduling
@EnableAsync
public class SchedulingConfig {
    private final int corePoolSize=2;
    private final int maxPoolSize=10;
    private final int queueCapacity=25;
    private final String namePrefix="AsyncTask-";

    /**
     * 自定义线程池配置类。
     * 不要命名为 taskScheduler,与spring框架的bean重名。
     * @return
     */
    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        //阿里巴巴编程规范:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

        //SpringBoot项目,可使用Spring提供的对 ThreadPoolExecutor 封装的线程池 ThreadPoolTaskExecutor:
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//        ThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();//自定义ThreadPoolTaskExecutor,会打印线程池情况
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        //     1、CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行。
        //        "该策略既不会抛弃任务,也不会抛出异常,而是将任务回推到调用者。"顾名思义,在饱和的情况下,调用者会执行该任务(而不是由多线程执行)
        //     2、AbortPolicy:拒绝策略,直接拒绝抛出异常
        //     3、。。。
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

第三步:编写定时任务业务层代码

package com.example.demo.task;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.xml.crypto.Data;
import java.util.concurrent.Executor;


@Component
public class BusinessTaskScheduler {



    @Scheduled(cron = "0 0/1 * * * ?")
    @Async("asyncServiceExecutor")
    public void TelecomBusinessTask() {
        // 业务逻辑代码
        try {
            System.out.println("任务1执行时间:"+System.currentTimeMillis());
            for (int i = 0; i < 6; i++) {

                System.out.println("执行任务的线程名称: " + Thread.currentThread().getName()+"-----任务1");
            }
        } catch (Exception e) {
            throw new RuntimeException("获取定时任务调用失败!"+e);
        }
    }



    @Scheduled(cron = "0 0/1 * * * ?")
    @Async("asyncServiceExecutor")
    public void MobileBeiXiangTask() {
        try {
            System.out.println("任务2执行时间:"+System.currentTimeMillis());
            for (int i = 0; i < 6; i++) {
                System.out.println("执行任务的线程名称: " + Thread.currentThread().getName()+"-----任务2");
            }
        } catch (Exception e) {
            throw new RuntimeException("获取定时任务调用失败!"+e);
        }
    }

}

测试结果


网站公告

今日签到

点亮在社区的每一天
去签到