Ref

  • SpringBoot timing task implementation ways | CSDN
  • Timing task framework Quartz – (a) Quartz introduction and Demo build | CSDN

This section describes how to implement scheduled tasks

  • Mode 1: Based onjava.util.TimerTimer, the realization of a timer similar to the alarm clock task
  • Method 2: Use open-source third-party scheduled task frameworks such as Quartz, elastice-Job, and XXl-Job, which are suitable for distributed project applications. The disadvantage of this approach is complex configuration.
  • Option 3: Use an annotation provided by Spring@Schedule, the development is simple, convenient to use.

Java.util. Timer Implements scheduled tasks

Based on the java.util.Timer Timer, it realizes the Timer task similar to the alarm clock. This approach is rarely used in projects, as shown in the following Demo.

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class SpringbootAppApplication {

	/** * main method *@param args
	 */
	public static void main(String[] args) {
		SpringApplication.run(SpringbootAppApplication.class, args);
		System.out.println("Server is running ...");

		TimerTask timerTask =  new TimerTask() {
			@Override
			public void run(a) {
				System.out.println("task run:"+ newDate()); }}; Timer timer =new Timer();
		timer.schedule(timerTask,10.3000); }}Copy the code

ScheduledExecutorService Implements scheduled tasks

This method is similar to Timer. See the following Demo.

public class TestScheduledExecutorService {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

        / * * *@paramCommand the task to execute the task body@paramInitialDelay The time to delay first execution *@paramPeriod the period between executions *@paramUnit The time unit of the initialDelay and period parameters Interval unit */
		service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0.3, TimeUnit.SECONDS);
        service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0.3, TimeUnit.SECONDS); }}Copy the code

@schedule Implements scheduled tasks

  • Spring Schedule implementation timing task | dead simple

Demo

  1. First, add on the project startup class@EnableSchedulingAnnotation to enable support for scheduled tasks.@EnableSchedulingThe purpose of annotations is to discover annotations@ScheduledAnd perform the task in the background.
@SpringBootApplication
@EnableScheduling
public class ScheduledApplication {

	public static void main(String[] args) { SpringApplication.run(ScheduledApplication.class, args); }}Copy the code
  1. Write the scheduled task class and methods. The scheduled task class is loaded through Spring IOC and used@Componentannotations
  2. Use of timing method@ScheduledAnnotation. In the following code,fixedRatelongType, indicating the number of milliseconds between tasks. The scheduled tasks in the following code are executed every 3 seconds.
@Component
public class ScheduledTask {

    @Scheduled(fixedRate = 3000)
    public void scheduledTask(a) {
        System.out.println("Task Execution time:"+ LocalDateTime.now()); }}Copy the code
  1. The project startup and running logs are as follows. The logs are generated every 3 seconds.
Server is running ... -ScheduledTask: 2020-06-23T18:02:14.747 Task execution time -ScheduledTask: 2020-06-23T18:02:17.748 Task execution time -ScheduledTask: 2020-06-23T18:02:17.748 2020-06-23T18:02:20.746 Task execution time -ScheduledTask: 2020-06-23T18:02:23.747Copy the code

@ Scheduled annotations

In the Demo above, the @scheduled (fixedRate = 3000) annotation was used to define tasks to be executed every 3 seconds. The use of @scheduled can be summarized in several ways

  • @Scheduled(fixedRate = 3000): 3 seconds after the last start time (fixedRateAttribute: Delay for executing the scheduled task again after the scheduled task starts (in milliseconds)
  • @Scheduled(fixedDelay = 3000): 3 seconds after the last execution time (fixedDelayAttribute: Delay for executing a scheduled task again after the scheduled task is completed (in milliseconds)
  • @Scheduled(initialDelay = 1000, fixedRate = 3000): Executes after the first delay of 1 second and then pressesfixedRateIs executed every 3 seconds (initialDelayAttribute: Delay time for executing the scheduled task for the first timefixedDelayorfixedRateTo use)
  • @Scheduled(cron="0 0 2 1 * ? * ")Through:cronExpression definition rules

Multithreading performs scheduled tasks

Create multiple scheduled tasks and print thread names as shown in the following code example.

import org.slf4j.LoggerFactory;

@Component
public class ScheduledTask {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ScheduledTask.class);

    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled(a){
        logger.info("Using cron-- Task execution time: {} Thread name: {}",LocalDateTime.now(),Thread.currentThread().getName());
    }
    
    @Scheduled(fixedRate = 5000)
    public void scheduled1(a) {
        logger.info("FixedRate -- Task execution time: {} Thread name: {}",LocalDateTime.now(),Thread.currentThread().getName());
    }
    @Scheduled(fixedDelay = 5000)
    public void scheduled2(a) {
        logger.info("FixedDelay -- Task execution time: {} Thread name: {}",LocalDateTime.now(),Thread.currentThread().getName()); }}Copy the code

The program output is as follows.

2020-06-23 23:31:04.447 INFO 34069: Scheduling -1 2020-06-23 23:31:04.494 INFO 34069: Scheduling -1 2020-06-23 23:31:04.494 INFO: Scheduling -1 2020-06-23 23:31:05.004 INFO 34069: Scheduling -1 2020-06-23 23:31:05.004 INFO: Scheduling -1 2020-06-23 23:31:09.445 INFO 34069: Scheduling: 2020-06-23T23:31:05.004 scheduling: 1 2020-06-23 23:31:09.445 INFO: Scheduling -1 2020-06-23 23:31:09.498 INFO 34069: Scheduling -1 2020-06-23 23:31:09.498 INFO Scheduling -1 2020-06-23 23:31:10.003 INFO 34069: Scheduling -1 2020-06-23 23:31:10.003 INFO Scheduling -1 2020-06-23 23:31:14.444 INFO 34069: Scheduling -1 2020-06-23 23:31:14.444 INFO Scheduling -1 2020-06-23 23:31:14.503 INFO 34069: Scheduling -1 2020-06-23 23:31:14.503 INFO Scheduling -1 2020-06-23 23:31:15.002 INFO 34069: Scheduling -1 2020-06-23 23:31:15.002 INFO Scheduling - Task execution time: 2020-06-23T23:31:15.002 Thread name: Scheduling -1Copy the code

As you can see, all three scheduled tasks have been executed and are being executed sequentially in the same thread. If the number of scheduled tasks increases, if one task is blocked, other tasks cannot be executed.

Therefore, you need to consider the case of multiple threads executing scheduled tasks.

  1. Create a configuration class: In a traditional Spring project, you can add the configuration of a Task to an XML configuration file. In a Spring Boot project, you usually add the configuration to a config class, so create a new oneAsyncConfigClass.In the configuration class, use@EnableAsyncAnnotations turn on support for asynchronous events.
package com.lbs0912.spring.demo.app;

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

@Configuration
@EnableAsync

public class AsyncConfig {

    private int corePoolSize = 10;
    private int maxPoolSize = 200;
    private int queueCapacity = 10;

    @Bean
    public Executor taskExecutor(a) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.initialize();
        returnexecutor; }}Copy the code
  • @Configuration: indicates that the class is a configuration class
  • @EnableAsync: Enables asynchronous event support
  1. Add to the class or method of the scheduled task@AsyncAnnotations that represent timed tasks that are asynchronous events.
@Component
@Async
public class ScheduledTask {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ScheduledTask.class);
    

    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled(a){
        logger.info("Use cron thread name: {}",Thread.currentThread().getName());
    }
    
    @Scheduled(fixedRate = 5000)
    public void scheduled1(a) {
        logger.info("FixedRate -- Thread name: {}",Thread.currentThread().getName());
    }
    @Scheduled(fixedDelay = 5000)
    public void scheduled2(a) {
        logger.info("FixedDelay thread name: {}",Thread.currentThread().getName()); }}Copy the code
  1. Running the program, the console output is as follows, and you can see that the scheduled task is executed in multiple threads.
2020-06-23 23:45:08.514  INFO 34824: fixedRate-- Thread name: taskExecutor-1
2020-06-23 23:45:08.514  INFO 34824: fixedDelay Thread name: taskExecutor-2
2020-06-23 23:45:10.005  INFO 34824: Uses cron thread name: taskExecutor-3

2020-06-23 23:45:13.506  INFO 34824: fixedRate-- Thread name: taskExecutor-4
2020-06-23 23:45:13.510  INFO 34824: fixedDelay Thread name: taskExecutor-5
2020-06-23 23:45:15.005  INFO 34824: Uses cron thread name: taskExecutor-6

2020-06-23 23:45:18.509  INFO 34824: fixedRate-- Thread name: taskExecutor-7
2020-06-23 23:45:18.511  INFO 34824: fixedDelay Thread name: taskExecutor-8
2020-06-23 23:45:20.005  INFO 34824: Uses cron thread name: taskExecutor-9

2020-06-23 23:45:23.509  INFO 34824: fixedRate-- Thread name: taskExecutor-10
2020-06-23 23:45:23.511  INFO 34824: fixedDelay Thread name: taskExecutor-1
2020-06-23 23:45:25.005  INFO 34824: Uses cron thread name: taskExecutor-2

2020-06-23 23:45:28.509  INFO 34824: fixedRate-- Thread name: taskExecutor-3
2020-06-23 23:45:28.512  INFO 34824: fixedDelay Thread name: taskExecutor-4
2020-06-23 23:45:30.005  INFO 34824: Uses cron thread name: taskExecutor-5

2020-06-23 23:45:33.509  INFO 34824: fixedRate-- Thread name: taskExecutor-6
2020-06-23 23:45:33.513  INFO 34824: fixedDelay Thread name: taskExecutor-7
2020-06-23 23:45:35.005  INFO 34824: Uses cron thread name: taskExecutor-8.Copy the code

Quartz implements timed tasks

Basic

  • Quartz website

Quartz is an open source project, developed entirely in Java, that can be used to perform timed tasks, similar to java.util.timer. But Quartz has a lot more functionality than Timer

  • Persistent jobs – that is, keep the state of the scheduled time
  • Job management – Effective management of scheduled jobs

Quartz can be divided into three basic parts, as shown below

  1. Task:JobDetailSpecific scheduled tasks
  2. Trigger:Trigger, includingSimpleTriggerCronTrigger. A trigger is responsible for triggering scheduled tasks. The most basic function of a trigger is to specify the execution time, execution interval, and running times of a Job.
  3. The scheduler:SchedulerTo implement how to specify triggers to execute the specified task.

Demo-1

  1. Add the dependent

Add the spring-boot-starter-Quartz dependency.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
Copy the code

For SpringBoot versions earlier than 1.5.9, you need to add the following dependencies.

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
</dependency>
Copy the code
  1. Create a scheduled task classJobQuartz, class inheritanceQuartzJobBeanAnd writeexecuteInternalMethods.
package com.lbs0912.spring.demo.app.quartz;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;

/ * * *@author lbs
 */
public class JobQuartz extends QuartzJobBean {

    /** * Execute scheduled tasks *@param jobExecutionContext  jobExecutionContext
     * @throws JobExecutionException JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("quartz task: " +  newDate()); }}Copy the code
  1. Creating a Configuration ClassQuartzConfig
package com.lbs0912.spring.demo.app.quartz;


import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/ * * *@author lbs
 */
@Configuration
public class QuartzConfig {
    @Bean
    // Create JobDetail instance
    public JobDetail testJobDetail(a){
        return JobBuilder.newJob(JobQuartz.class).withIdentity("jobQuartz").storeDurably().build();
    }
    @Bean
    public Trigger testTrigger(a){
     
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(10)  // Set the time period in seconds
                .repeatForever();
        // Build Trigger instances every 10 seconds
        return TriggerBuilder.newTrigger().forJob(testJobDetail())
                .withIdentity("jobQuartz") .withSchedule(scheduleBuilder) .build(); }}Copy the code
  1. Start the project and view the log output.
Server is running ...
quartz task: Wed Jun 24 00:49:07 CST 2020
quartz task: Wed Jun 24 00:49:17 CST 2020
quartz task: Wed Jun 24 00:49:27 CST 2020
Copy the code

Demo-2

  • Timing task framework Quartz – (a) Quartz introduction and Demo build | CSDN
  1. Add the dependent
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
Copy the code
  1. Creating a TaskPrintWordsJobClasses that implementJobInterface, overrideexecuteMethods.
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

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

/ * * *@author liubaoshuai1
 */
public class PrintWordsJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100)); }}Copy the code
  1. Create a schedulerScheduleAnd create a trigger in the classTriggerInstance to perform a task.
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

@SpringBootApplication
@EnableScheduling
public class SpringbootAppApplication {

    /** * main method **@param args
     */
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        System.out.println("Server is running ...");

        // Create a Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();

        // Create JobDetail instance and bind it to PrintWordsJob
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
                .withIdentity("job1"."group1").build();

        // create Trigger instance every 1s
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1"."triggerGroup1")
                .startNow()// Effective immediately
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
						.withIntervalInSeconds(1)// The command is executed every 1s
                        .repeatForever()).build();// always execute

        / / 4, execution
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------scheduler start ! -- -- -- -- -- -- -- -- -- -- -- --");
        scheduler.start();

        / / sleep
        TimeUnit.MINUTES.sleep(1);
        scheduler.shutdown();
        System.out.println("--------scheduler shutdown ! -- -- -- -- -- -- -- -- -- -- -- --"); }}Copy the code
  1. When you run the program, you can see that it prints out every 1s and ends one minute later.
[DefaultQuartzScheduler_Worker 11:05:27. 551-9] the DEBUG org. Quartz. Core. JobRunShell - Calling the execute on job group1. Job1 is PrintWordsJob start at:20-06-24 11-05-27, prints: Hello Job - 5 11:05:28. [10] DefaultQuartzScheduler_Worker - 548 the DEBUG org. Quartz. Core. JobRunShell - Calling the execute on the Job group1.job1 PrintWordsJob start at:20-06-24 11-05-28, prints: Hello Job - 56 11:05:29. [548] DefaultQuartzScheduler_Worker - 1 the DEBUG org. Quartz. Core. JobRunShell - Calling the execute on the Job group1.job1 PrintWordsJob start at:20-06-24 11-05-29, prints: Hello 11:05:29 Job - 82. 550. [the main] INFO org. Quartz. Core. The QuartzScheduler - the Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.
--------scheduler shutdown ! ------------
Copy the code

Quartz code analysis

The following is a description of several parameters in the Quartz framework based on the Demo-2 project.

  • The Job and the JobDetail
  • JobExecutionContext
  • JobDataMap
  • Trigger, SimpleTrigger, CronTrigger

Job

Job is an interface in Quartz. Under the interface, only the execute method is used to write service logic.

package org.quartz;

public interface Job {
    void execute(org.quartz.JobExecutionContext jobExecutionContext) throws org.quartz.JobExecutionException;
}
Copy the code

JobDetail

The JobDetail is used to bind the Job and provide many attributes for the Job instance

  • name
  • group
  • jobClass
  • jobDataMap

JobDetail binds the specified Job. Each time the Scheduler executes a Job, it obtains the corresponding Job, creates the Job instance, and executes execute() in the Job. After the task is executed, the associated Job object instance is released and cleared by the JVM GC.

Why is it designed to beJobDetail + Job, not directly usedJob

The JobDetail defines the task data, while the real execution logic is in the Job. This is because tasks can be executed concurrently, and if the Scheduler uses the Job directly, there is the problem of concurrent access to the same Job instance. With JobDetail + Job, Sheduler creates a new Job instance based on JobDetail every time it executes, thus avoiding the problem of concurrent access.

JobExecutionContext

JobExecutionContext contains detailed data about the Quartz runtime environment and the Job itself. When a Job is executed by Schedule, the JobExecutionContext is passed to the Job’s execute(), and the Job gets information from the JobExecutionContext object.

JobExecutionContext provides the following information

Trigger

Trigger is a Quartz Trigger that informs the Scheduler when to execute the corresponding Job.

  • new Trigger().startAt(): Indicates the time when the trigger is triggered for the first time
  • new Trigger().endAt(): Indicates the time when the trigger ends

SimpleTrigger can execute a job once within a specified period of time or multiple times within a period of time. CronTrigger is calendar-based job scheduling, while SimpleTrigger specifies precise intervals, so CroTrigger is more commonly used than SimpleTrigger. CroTrigger is based on Cron expressions.

Cron expression

Ref

  • | the Denver nuggets, a regular task of cron expression
  • Spring Task cron expression details
  • Cron visualization tool
  • IDEA – Cron Description plug-in

Expression definition

A CRon expression is a string divided by six Spaces into seven fields, each representing a time meaning. Usually the part that defines “year” can be omitted, and the actual Cron expression commonly used consists of the first six parts. Format is as follows

[second] [minute] [hour] [day] [month] [week] [year]Copy the code
The domain If required Values and ranges The wildcard
seconds is 0-59 , – * /
points is 0-59 , – * /
when is 0-23 , – * /
day is 1-31 , – *? / L W
month is 1-12 or JAN – DEC , – * /
weeks is 1-7 – SAT or SUN , – *? / L #
years no 1970-2099. , – * /

It should be noted that in the Cron expression, “week” is calculated from Sunday. 1 in the week field means Sunday, and 7 means Saturday.

Wildcard character in Cron

  • .: refers to execution at more than two points in time. If I define theta in the division domain8,12,35, indicates that the scheduled task is executed at minutes 8 and 12 and 35.
  • -: Specifies the contiguous range within a domain. If I define it in the time field1-6Is triggered every hour from 1 to 6 o ‘clock, equivalent to6
  • *: indicates all values, which can be interpreted as each. If set in the day field*Is triggered every day.
  • ?: Indicates that no value is specified. Use scenarios that do not care about the current value of this field. For example, if we want to trigger an action on the 8th of the month, but don’t care what day of the week it is, we can set it this way0, 0, 0, 8 times?
  • /: indicates the trigger step."/"The preceding value represents the initial value ("*"equivalent"0"), and the following values represent offsets that are fired periodically on a field. Let’s say we define it in seconds5/10The value is executed every 10 seconds starting from the fifth second. In Fen, the command is executed every 10 minutes starting from the fifth minute.
  • L: indicates in EnglishLASTCan only be used in day and week. Set in “Day”, indicating the last day of the month (depending on the current month, or if it is February, depending on whether it is a calendar year), and in “week” indicating Saturday, which is equivalent to “7” or “SAT”. If the “L” is preceded by a number, it indicates the last of the data. For example, if the format of “7L” is set on “Week”, it means “the last Saturday of the month”.
  • W: Indicates the latest working day (Monday to Friday) before the specified date. This parameter can be used only in Days and after a specific digit. If 15W is set to Day, it is triggered on the working day nearest the 15th of each month. If the 15th falls on a Saturday, the nearest Friday (14th) is used. If the 15th is the weekend, the next Monday (16th) will be triggered. If the 15th falls on a weekday (Monday to Friday), it is triggered on that day. If it’s “1W”, it can only be pushed to the next working day of the month, not to the next month.
  • #: e.g.,A#BRepresents the first day of the monthBA week of the weekA(Week count starts on Sunday), can only be used on weeks. For example,2 # 3On the third Tuesday of every month,5 # 3Represents the Thursday of the third week of the month.

Note that L is used in the “week” field, where the last day of the week is Saturday. 1 in the Week field means Sunday, and 7 means Saturday, which means the weekly count starts on Sunday.

Visualization tool

  • IDEA – Cron Description plug-in
  • Cron visualization tool

Click “Reverse Parsing to UI” on the website of the visualization tool, and you can view the running time of the scheduled task for the last five times, which is easy to understand.

In addition, in IDEA, the Cron Description plug-in can also be installed to display the visual semantics, as shown in the following figure. Hover the mouse over the Cron expression to see the visual semantics.

The sample

Some examples are given below, which can be interpreted according to the above explanation.

  • Every night at 12 PM:0, 0, 0 * *?
  • Run this command every 1 minute:0 */1 * * *?
  • Once at 1:00 a.m. on the first day of each month:0, 0, 1, 1 star?
  • Once at 23:00 on the last day of each month:0 0 23 L * ?
  • Once every Saturday at 3am:0 0 3? * L
  • Execute once at 24 and 30:0 24,30 * * *?