乐趣区

关于java:Spring-Boot-并行任务这才是优雅的实现方式

Spring Boot 的定时工作:

第一种:把参数配置到.properties 文件中:

代码:

package com.accord.task;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
/**
 * 从配置文件加载工作信息
 * @author 王久印
 */
@Component
public class ScheduledTask {private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
 
  //@Scheduled(fixedDelayString = "${jobs.fixedDelay}")
  @Scheduled(fixedDelayString = "2000")
  public void getTask1() {System.out.println("工作 1, 从配置文件加载工作信息,以后工夫:" + dateFormat.format(new Date()));
  }
 
  @Scheduled(cron = "${jobs.cron}")
  public void getTask2() {System.out.println("工作 2, 从配置文件加载工作信息,以后工夫:" + dateFormat.format(new Date()));
  }
}

application.properties 文件:

jobs.fixedDelay=5000
jobs.cron=0/5 * *  * * ?

SpringBootCron2Application.java 中:

package com.accord;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
@SpringBootApplication
@EnableScheduling
public class SpringBootCron2Application {public static void main(String[] args) {SpringApplication.run(SpringBootCron2Application.class, args);
 }
}

Spring Boot 根底就不介绍了,举荐看这个收费教程:

https://github.com/javastacks/spring-boot-best-practice

注:@EnableScheduling 这个肯定要加上;否则,不会定时启动工作!

@Scheduled 中的参数阐明:

  • @Scheduled(fixedRate=2000):上一次开始执行工夫点后 2 秒再次执行;
  • @Scheduled(fixedDelay=2000):上一次执行结束工夫点后 2 秒再次执行;
  • @Scheduled(initialDelay=1000, fixedDelay=2000):第一次提早 1 秒执行,而后在上一次执行结束工夫点后 2 秒再次执行;
  • @Scheduled(cron="* * * * * ?"):按 cron 规定执行。

第二种定时工作:单线程和多线程

1、创立定时工作:

package com.accord.task;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
/**
 * 构建执行定时工作
 * @author 王久印
 * TODO
 */
@Component
public class ScheduledTask2 {private Logger logger = LoggerFactory.getLogger(ScheduledTask2.class);
 
    private int fixedDelayCount = 1;
    private int fixedRateCount = 1;
    private int initialDelayCount = 1;
    private int cronCount = 1;
 
    @Scheduled(fixedDelay = 5000)        //fixedDelay = 5000 示意以后办法执行结束 5000ms 后,Spring scheduling 会再次调用该办法
    public void testFixDelay() {logger.info("===fixedDelay: 第 {} 次执行办法", fixedDelayCount++);
    }
 
    @Scheduled(fixedRate = 5000)        //fixedRate = 5000 示意以后办法开始执行 5000ms 后,Spring scheduling 会再次调用该办法
    public void testFixedRate() {logger.info("===fixedRate: 第 {} 次执行办法", fixedRateCount++);
    }
 
    @Scheduled(initialDelay = 1000, fixedRate = 5000)   //initialDelay = 1000 示意提早 1000ms 执行第一次工作
    public void testInitialDelay() {logger.info("===initialDelay: 第 {} 次执行办法", initialDelayCount++);
    }
 
    @Scheduled(cron = "0 0/1 * * * ?")  //cron 承受 cron 表达式,依据 cron 表达式确定定时规定
    public void testCron() {logger.info("===initialDelay: 第 {} 次执行办法", cronCount++);
    }
 
}

应用 @Scheduled 来创立定时工作 这个注解用来标注一个定时工作办法。

通过看 @Scheduled 源码能够看出它反对多种参数:

  • cron:cron 表达式,指定工作在特定工夫执行;
  • fixedDelay:示意上一次工作执行实现后多久再次执行,参数类型为 long,单位 ms;
  • fixedDelayString:与 fixedDelay 含意一样,只是参数类型变为 String;
  • fixedRate:示意按肯定的频率执行工作,参数类型为 long,单位 ms;
  • fixedRateString: 与 fixedRate 的含意一样,只是将参数类型变为 String;
  • initialDelay:示意提早多久再第一次执行工作,参数类型为 long,单位 ms;
  • initialDelayString:与 initialDelay 的含意一样,只是将参数类型变为 String;
  • zone:时区,默认为以后时区,个别没有用到。

2、开启定时工作:

package com.accord;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
@SpringBootApplication
@EnableScheduling
public class SpringBootCron2Application {public static void main(String[] args) {SpringApplication.run(SpringBootCron2Application.class, args);
 }
}

注:这里的 @EnableScheduling 注解,它的作用是发现注解 @Scheduled 的工作并由后盾执行。没有它的话将无奈执行定时工作。

援用官网文档原文:

@EnableScheduling ensures that a background task executor is created. Without it, nothing gets scheduled.

3、执行后果(单线程)

就实现了一个简略的定时任务模型,上面执行 springBoot 察看执行后果:

从控制台输出的后果中咱们能够看出所有的定时工作都是在同一个线程池用同一个线程来解决的,那么咱们如何来并发的解决各定时工作呢,请持续向下看。

4、多线程解决定时工作:

看到控制台输入的后果,所有的定时工作都是通过一个线程来解决的,我预计是在定时工作的配置中设定了一个 SingleThreadScheduledExecutor,于是我看了源码,从ScheduledAnnotationBeanPostProcessor 类开始一路找上来。果然,在 ScheduledTaskRegistrar(定时工作注册类)中的ScheduleTasks 中又这样一段判断:

if (this.taskScheduler == null) {this.localExecutor = Executors.newSingleThreadScheduledExecutor();
 this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}

这就阐明如果 taskScheduler 为空,那么就给定时工作做了一个单线程的线程池,正好在这个类中还有一个设置 taskScheduler 的办法:

public void setScheduler(Object scheduler) {Assert.notNull(scheduler, "Scheduler object must not be null");
 if (scheduler instanceof TaskScheduler) {this.taskScheduler = (TaskScheduler) scheduler;
 }
 else if (scheduler instanceof ScheduledExecutorService) {this.taskScheduler = new ConcurrentTaskScheduler(((ScheduledExecutorService) scheduler));
 }
 else {throw new IllegalArgumentException("Unsupported scheduler type:" + scheduler.getClass());
 }
}

这样问题就很简略了,咱们只需用调用这个办法显式的设置一个 ScheduledExecutorService 就能够达到并发的成果了。咱们要做的仅仅是实现 SchedulingConfigurer 接口,重写 configureTasks 办法就 OK 了;

package com.accord.task;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
 
import java.util.concurrent.Executors;
 
/**
 * 多线程执行定时工作
 * @author 王久印
 */
@Configuration
// 所有的定时工作都放在一个线程池中,定时工作启动时应用不同都线程。public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 设定一个长度 10 的定时工作线程池
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }
}

5、执行后果(并发)

通过控制台输入的后果看出每个定时工作都是在通过不同的线程来解决了。

起源:wangjiuyin.blog.csdn.net/article/details/79411952

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

退出移动版