Quartz Thread Processing

Keywords: Apache Java Spring

Quartz scheduled tasks are executed concurrently by default. They will not wait for the last task to finish. They will be executed as long as the interval arrives. If the scheduled tasks are executed too long, they will occupy resources for a long time, resulting in other tasks blocking.
1. In Spring, you need to set the concurrent value to false to prohibit concurrent execution.

 <property name="concurrent" value="true" />

2. When you don't use spring, you need to add @DisallowConcurrent Execution annotation to Job's implementation class
@ DisallowConcurrent Execution prohibits concurrent execution of multiple JobDetails with the same definition. This annotation is added to the Job class, but it does not mean that multiple Jobs cannot be executed simultaneously, but that the same Job Definition (defined by JobDetail) can be executed concurrently, but multiple JobDetails can be executed simultaneously. For example, we have a Job class, SayHelloJob, and in this Job class, SayHelloJob can be executed concurrently. Add this annotation, and then define a number of JobDetails on the Job, such as Say Hello ToJoe JobDetail, Say Hello ToMike JobDetail. When scheduler starts up, multiple Say Hello ToJoe JobDetail or Say Hello ToMike JobDetail will not be executed concurrently, but both Say Hello ToJoe JobDetail and Say Hello ToMike JobDetail can be executed simultaneously.

@ Persist Job Data AfterExecution is also added to Job, indicating that when Job is executed properly, the data in JobDataMap should be changed to be used for the next call. When using the @PersistJobDataAfterExecution annotation, it is strongly recommended that the @DisallowConcurrent Execution annotation be added to avoid confusion when storing data in parallel.

 

 

@DisallowConcurrentExecution

This tag is used to implement Job classes, meaning that concurrent execution is not allowed. According to my previous understanding, the scheduling framework is not allowed to call Job classes at the same time. Later, it was found that this is not the case, but that the execution time of Job (tasks) [such as 10 seconds] is greater than the time interval of tasks [Interval (5 seconds)]. By default, the scheduling framework is designed to enable tasks to follow me. When executed at a predetermined interval, new threads are immediately enabled to perform tasks. Otherwise, it will wait for the task to be completed before it is re-executed! (This results in tasks not being executed at pre-defined intervals)

Test code, which is an official example. The time interval is set to 3 seconds, but the job execution time is 5 seconds. After setting @DisallowConcurrent Execution, the program will wait until the task is finished, otherwise the new thread execution will be enabled in 3 seconds.

org.quartz.threadPool.threadCount = 5 The number of threads in the thread pool of the configuration framework here should be configured several more, otherwise @DisallowConcurrent Execution will not work.
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 5
org.quartz.jobStore.class =org.quartz.simpl.RAMJobStore
/*
 * Copyright 2005 - 2009 Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 */

package org.quartz.examples.example5;

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;

/**
 * <p> A dumb implementation of Job, for unit testing purposes. </p>
 * 
 * @author James House
 */
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class StatefulDumbJob implements Job {

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Constants.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

    public static final String NUM_EXECUTIONS = "NumExecutions";

    public static final String EXECUTION_DELAY = "ExecutionDelay";

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Constructors.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

    public StatefulDumbJob() {
    }

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Interface.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

    /**
     * <p> Called by the <code>{@link org.quartz.Scheduler}</code> when a <code>{@link org.quartz.Trigger}</code> fires that is associated with the <code>Job</code>. </p>
     * 
     * @throws JobExecutionException if there is an exception while executing the job.
     */
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.err.println("---" + context.getJobDetail().getKey() + " executing.[" + new Date() + "]");

        JobDataMap map = context.getJobDetail().getJobDataMap();

        int executeCount = 0;
        if (map.containsKey(NUM_EXECUTIONS)) {
            executeCount = map.getInt(NUM_EXECUTIONS);
        }

        executeCount++;

        map.put(NUM_EXECUTIONS, executeCount);

        long delay = 5000l;
        if (map.containsKey(EXECUTION_DELAY)) {
            delay = map.getLong(EXECUTION_DELAY);
        }

        try {
            Thread.sleep(delay);
        } catch (Exception ignore) {
        }

        System.err.println(" -" + context.getJobDetail().getKey() + " complete (" + executeCount + ").");

    }

}
/*
 * Copyright 2005 - 2009 Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 */

package org.quartz.examples.example5;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.DateBuilder.*;

import java.util.Date;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Demonstrates the behavior of <code>StatefulJob</code>s, as well as how misfire instructions affect the firings of triggers of <code>StatefulJob</code> s - when the jobs take longer to execute that the frequency of the trigger's repitition.
 * 
 * <p> While the example is running, you should note that there are two triggers with identical schedules, firing identical jobs. The triggers "want" to fire every 3 seconds, but the jobs take 10 seconds to execute. Therefore, by the time the jobs complete their execution, the triggers have already "misfired" (unless the scheduler's "misfire threshold" has been set to more than 7 seconds). You should see that one of the jobs has its misfire instruction set to <code>SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT</code>, which causes it to fire immediately, when the misfire is detected. The other trigger uses the default "smart policy" misfire instruction, which causes the trigger to advance to its next fire time (skipping those that it has missed) - so that it does not refire immediately, but rather at the next scheduled time. </p>
 * 
 * @author <a href="mailto:bonhamcm@thirdeyeconsulting.com">Chris Bonham</a>
 */
public class MisfireExample {
    public void run() throws Exception {
        Logger log = LoggerFactory.getLogger(MisfireExample.class);

        log.info("------- Initializing -------------------");

        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();

        log.info("------- Initialization Complete -----------");

        log.info("------- Scheduling Jobs -----------");

        // jobs can be scheduled before start() has been called

        // get a "nice round" time a few seconds in the future...
        Date startTime = nextGivenSecondDate(null, 15);

        // statefulJob1 will run every three seconds
        // (but it will delay for ten seconds)
        JobDetail job = newJob(StatefulDumbJob.class).withIdentity("statefulJob1", "group1").usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L).build();

        SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();

        Date ft = sched.scheduleJob(job, trigger);
        log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");

        log.info("------- Starting Scheduler ----------------");

        // jobs don't start firing until start() has been called...
        sched.start();

        log.info("------- Started Scheduler -----------------");

        try {
            // sleep for ten minutes for triggers to file....
            Thread.sleep(600L * 1000L);
        } catch (Exception e) {
        }

        log.info("------- Shutting Down ---------------------");

        sched.shutdown(true);

        log.info("------- Shutdown Complete -----------------");

        SchedulerMetaData metaData = sched.getMetaData();
        log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
    }

    public static void main(String[] args) throws Exception {

        MisfireExample example = new MisfireExample();
        example.run();
    }

}

 

 

@PersistJobDataAfterExecution

This tag indicates that after the execution method of Job is executed, the fixed data in JobDataMap is saved. By default, when @PersistJobDataAfterExecution is not set, each job has a separate JobDataMap.

Otherwise, the change task will have the same JobDataMap when repeated execution occurs.

 

/*
 * Copyright 2005 - 2009 Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 */

package com.quartz.demo.example6;

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BadJob1 implements Job {

    public BadJob1() {
    }

    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();

        int denominator = dataMap.getInt("denominator");
        System.out.println("---" + jobKey + " executing at " + new Date() + " with denominator " + denominator);

        denominator++;
        dataMap.put("denominator", denominator);
    }

}

 

 

/*
 * Copyright 2005 - 2009 Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 */

package com.quartz.demo.example6;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.DateBuilder.*;

import java.util.Date;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;

public class JobExceptionExample {

    public void run() throws Exception {

        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();

        // jobs can be scheduled before start() has been called

        // get a "nice round" time a few seconds in the future...
        Date startTime = nextGivenSecondDate(null, 2);

        JobDetail job = newJob(BadJob1.class).withIdentity("badJob1", "group1").usingJobData("denominator", "0").build();

        SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();

        Date ft = sched.scheduleJob(job, trigger);

        //Tasks are executed every 2 seconds so the data from the JobDataMap in the BadJob1 method is shared.
        //One thing to note here is that data sharing for JobDataMap is for only one BadJob1 task.
        //If a new task is added below, then they are not shared, such as the following

        JobDetail job2 = newJob(BadJob1.class).withIdentity("badJob1", "group1").usingJobData("denominator", "0").build();

        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();

        //This job 2 is not shared with the JobDataMap executed by job
        sched.scheduleJob(job2, trigger2);

        sched.start();

        try {
            // sleep for 30 seconds
            Thread.sleep(30L * 1000L);
        } catch (Exception e) {
        }

        sched.shutdown(false);
    }

    public static void main(String[] args) throws Exception {

        JobExceptionExample example = new JobExceptionExample();
        example.run();
    }

}

Posted by azhan on Sat, 20 Apr 2019 20:39:33 -0700