Introduction to the

Java back-end novice, shallow understanding of knowledge content, such as the content of the article is wrong, please give advice. This article is suitable for beginners who have a certain understanding of Quartz, and it does not adopt the official persistence method of Quartz. Instead, it completes the function of adding and modifying the quartz task schedule based on the work requirements, as well as the modification of the time expression


A task is something that you want to do, which is the core of task scheduling. For example, I want to drink a glass of water every hour, so the task is Trigger ‘drink water’ : Trigger A trigger refers to the time when a task is triggered, that is, when the task starts executing. For example, in the previous example of drinking water, “every hour”, this is a trigger that has two types of timing, a simple timing and a complex timing using Cron time expressions.



The Scheduler:The scheduler

The scheduler is used to receive tasks and triggers, to integrate corresponding tasks and triggers, and to implement remoting and clustering

Cron time expression:Often used in task scheduling to complete complex periodic time representations, such as (0 0/1 * * *? This means one task per minute. A Cron expression generator is recommended and handwriting is highly discouraged

Simple to use

Use of traditional SpringBoot configuration

This approach uses the SpringBoot/Quartz integration approach and automatically configures tasks and triggers as Bean components.

1. Import dependencies

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

2. Create a task class

Prior to SpringBoot2.x, creating a task class using Quartz required implementing the Job interface and overriding the exCute () method, whereas in SpringBoot, you typically inherited QuartzJobBean and overriding the executeInternal() method. The method is what the task actually does

@Slf4j
public class MyJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("MyJob is running!"); }}Copy the code

3. Create a configuration class

A Job needs to be created and associated with the corresponding entity class. Each Job and Trigger can be grouped. A Job/Trigger has the unique identifier of group and name, and a Trigger cannot have two jobs/triggers with the same identifier

  • Jobs and triggers can be grouped, and corresponding tasks and triggers can be grouped into the same group
  • You can use the group and name of the Trigger to get the corresponding Trigger
@Configuration
public class QuartzConfig {
    
    @Bean
    public JobDetail jobDetail(a){

        return JobBuilder.newJob(MyJob.class)// Load the task entity (required)
                .withIdentity("myJob"."group1")// Set the group
                .storeDurably(true)// True causes the task to exist without the corresponding trigger
                .build();
    }

    @Bean
    public Trigger trigger(a){
        // Set the Cron time expression. SimpleScheduleBuilder is also available
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/30 0/1 * * *?");
        // Create trigger
        return TriggerBuilder.newTrigger()
                .forJob(jobDetail())// Bind to Job
                .withIdentity("trigger"."group1")/ / group
                .withSchedule(cronScheduleBuilder)// Binding trigger time
                .build();/ / build}}Copy the code

When the program starts, the @configration configuration file is automatically loaded, along with the method tagged by @Bean. At this point, task scheduling starts. The following is implemented when the program starts, which means that the content of Quartz is stored in memory, not persisted, and shows information such as the number of threads

  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
Copy the code

Complex multitask scheduling

Gitee source address: Quartz task scheduling

1. Introduction

Multi-task scheduling is impossible to be concurrent, but the asynchronous and parallel method of multi-threading is adopted to schedule multiple tasks. Quartz has its own thread pool. By default, the number of core threads is 10, which means that up to 10 tasks can be scheduled simultaneously. When ten tasks are switched, the thread pool releases the free threads, starting with the first thread, and the loop repeats. However, it is not recommended to run 10 tasks without a thread pool configured. This can cause thread congestion.

2. Non-default persistence

Instead of using the default persistence approach, this article’s multitasking approach uses self-made tables to store task information. If you want to use the default persistence mode, refer to the Quartz official documentation for table creation. If you want to simply use the self-created table, the main requirements are as follows:

3. Create a data table

Each task class inherits from the QuartzJobBean class and overwrites the overall requirements of the corresponding method: to complete the scheduled scheduling of multiple tasks, and to be able to add tasks to the data table and modify the task execution cycle



The creation of a task class needs to correspond to the data in the data table, and the result of the task class needs to be configured

The contents of the data table are as follows:



The entity classpath must have a corresponding entity class. Make sure that the Job you want to add exists in your program before adding a task to the table.

4. Program structure

The program structure is as follows:



The core code is the two classes of the control layer

(1) Entity class: job.java

import lombok.Data;

@Data
public class Job {

    private int id;   / / id since
    private String group;   / / group
    private String name;    / / task name
    private String className;  // Task classpath
    private String cron;  // Time expression

}
Copy the code

(2) the task

MyJob/MyJob2 Before adding a task to the database, remember to create the corresponding task class. The executeInternal() method in the task class is the operation that your task will perform, which is omitted by logging instead.

@Slf4j
public class MyJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("MyJob is running!"); }}@Slf4j
public class MyJob2 extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("MyJob2 is up and running!); }}Copy the code

(3) the core classes

The quartzjob. Java class is the core class for task scheduling. It mainly includes four methods. The functions of these methods will be described in detail in the code

@Slf4j
@Component
public class QuartzJob {

    @Resource
    Scheduler scheduler;

    // Here is the data Service to get the database table
    @Resource
    JobService jobService;

    public void start(a) throws SchedulerException {
        scheduler.start();
    }

    @Scheduled(cron = "0 0/1 * * * ?" )
    public void loadJob(a) throws Exception {
        // The database traverses the data
        List<Job> list = jobService.findAllJob();

        for (Job job : list){

            // Retrieve the data, identity, and key of a trigger to be modified
            TriggerKey triggerKey = new TriggerKey(job.getName(),job.getGroup());

            // Get the specific CronTrigger to change based on key
            CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            If there is no corresponding trigger, create the task and trigger
            if (null == cronTrigger){
                createJob(scheduler,job);
            }else {
                // If the corresponding trigger already exists, the task already exists and the program moves to the trigger Cron monitoring methodupdateJob(job,job.getCron()); }}}public void createJob(Scheduler scheduler,Job job) throws SchedulerException {

        Class<org.quartz.Job> clazz;
        try {
            // This is the class that the scheduler will execute
            clazz = (Class<org.quartz.Job>) Class.forName(job.getClassName());
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException(e1);
        }

        // Create tasks in batches
        JobDetail jobDetail = JobBuilder.newJob(clazz)
                .withIdentity(job.getName(),job.getGroup())
                .build();
        // Get the current Cron time
        String cron = job.getCron();
        // Create an expression to build the trigger
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing();
		// Create trigger
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .forJob(jobDetail)
                .withSchedule(cronScheduleBuilder)
                .withIdentity(job.getName(),job.getGroup())
                .build();
        // The scheduler integrates tasks with their corresponding triggers
        scheduler.scheduleJob(jobDetail,cronTrigger);
        log.info("Current job created successfully: {}",job.getName());

    }

    /** * 1. This method updates the trigger, primarily by updating the Cron expression * 2. This method determines whether the current trigger time has changed from the last minute * 3. The expression is modified only if a change is determined * *@paramJob Task entity class *@paramTime Modified Cron expression *@throws Exception
     */
    public void updateJob(Job job,String time) throws Exception{

        // Create a trigger to modify the data, identity
        TriggerKey triggerKey = new TriggerKey(job.getName(),job.getGroup());

        // Get the specific CronTrigger trigger to change
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        // Get the current time
        String oldTime = cronTrigger.getCronExpression();
        if(! oldTime.equals(time)){// Update the trigger with the modified time
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time).withMisfireHandlingInstructionDoNothing();
            CronTrigger cronTrigger1 = TriggerBuilder.newTrigger()
                    .withIdentity(job.getName(),job.getGroup())
                    .withSchedule(cronScheduleBuilder)
                    .build();
            // The scheduler integrates the new trigger
            scheduler.rescheduleJob(triggerKey,cronTrigger1);
            log.info("Task '{}' has been modified after listening to the modification, the execution time before modification is {}, and the execution time after modification is {}",job.getName(),oldTime,time); }}}Copy the code

CreateJob /updateJob -> loadJob ->start createJob(); createJob/updateJob -> loadJob ->start createJob(); This method is used to update the trigger for the corresponding task class when the time expression is modified. LoadJob () : This method is used to perform the creation of the task and the modification of the trigger. When the method executes, it starts to check the corresponding name and group to see if the task and its trigger have been created. So when you add a task to the table and find the trigger corresponding to your teammate, you will create a new task and trigger. If it already exists, it goes to the trigger’s listening method, and changes the trigger only if the time expression is modified. The @Scheduled annotation for this method allows it to run at a fixed time, in this case one minute. Start () : is the start method of task scheduling, which starts the scheduler. The reason for writing it separately is to make sure that the scheduler is started only once and always runs stably


QuartzStart.java

@Slf4j
@Component
public class QuartzStart implements ApplicationListener<ApplicationEvent> {

    private static boolean loaded = false;

    @Resource
    QuartzJob quartzJob;

    // This method is executed once when the container is loaded
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if(applicationEvent instanceof ContextRefreshedEvent){
            if(! loaded){// Avoid multiple executions
                loaded = true;
                // Start the scheduled task
                try {
                    
                    // Load all tasks the first time
                    quartzJob.loadJob();
                    // All tasks are executed
                    quartzJob.start();
                    System.out.println("The mission has been initiated... La la la la la la la");

                } catch (SchedulerException se) {
                    se.printStackTrace();
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }

        }
    }
}
Copy the code

The methods of this class are loaded and executed only once, at server startup, in order to load the first round of tasks and start task scheduling

(4) Execution result

At the beginning, all tasks are created and executed according to the Cron expression in the task table, initially every 10 seconds



After modifying the expression, task 2 is executed once every 15 seconds



Here to further explain that in time to modify the listener, namely @ Scheduled annotation methods, time if set to the top of the hour (per minute), and the task execution coincidence (every 10 seconds, the sixth time for a whole minute), due to modify the trigger and the execution of a task class separate two threads, so when the time coincidence, evenThe time has been changed, but the task class is still executed at the time before the change, the second time will be executed according to the new time. In order to avoid such a situation, it is suggested to arrange the time reasonably.

In this paper, the modification of tasks and time is completed in the database, or it can be combined with the front end and encapsulated as an interface to dynamically modify the data in the database, so as to realize the control of scheduled tasks

In this paper, to the end.

New handwriting, I hope you find this article useful, if you have any questions or suggestions, please feel free to discuss in the comments section!