1 Introduction
According Quartz design, a Job can bind multiple Trigger, will inevitably encounter concurrency issues.
2 Concurrent
2.1 reproducibility
Let's write an example of concurrency:
1 /** 2 * @author pancc 3 * @version 1.0 4 */ 5 public class AcceptConcurrentDemo { 6 7 public static void main(String[] args) throws SchedulerException, InterruptedException { 8 JobDetail detail = JobBuilder.newJob(AcceptConcurrentJob.class) 9 .withIdentity("detail", "group0") 10 .build(); 11 12 13 Trigger trigger = TriggerBuilder.newTrigger() 14 .withIdentity("ben_trigger") 15 .usingJobData("name", "ben") 16 .startNow() 17 .build(); 18 19 Trigger triggers = TriggerBuilder.newTrigger() 20 .withIdentity("mike_trigger") 21 .usingJobData("name", "mike") 22 .forJob("detail", "group0") 23 .startNow() 24 .build(); 25 26 27 Scheduler scheduler = new StdSchedulerFactory().getScheduler(); 28 29 scheduler.start(); 30 scheduler.scheduleJob(detail, trigger); 31 scheduler.scheduleJob(triggers); 32 /* 33 * 6 秒钟后关闭 34 */ 35 Thread.sleep(6_000); 36 scheduler.shutdown(); 37 } 38 39 @Data 40 public static class AcceptConcurrentJob implements Job { 41 private String name; 42 43 @Override 44 public void execute(JobExecutionContext context) { 45 try { 46 System.out.printf("i am %s \n", name); 47 Thread.sleep(2_000); 48 } catch (InterruptedException e) { 49 e.printStackTrace(); 50 } 51 } 52 } 53 }
Please note that the top of the Details of Identity, is set to group0.detail, we created two the Trigger , the second trigger when it is created by specifying Identity bind to the target Job, then submit this the Job , and two Trigger, can We see two triggers simultaneously execute method of starting a Job
Code above may be simplified to the following form:
1 /** 2 * @author pancc 3 * @version 1.0 4 */ 5 public class AcceptConcurrentDemo { 6 7 public static void main(String[] args) throws SchedulerException, InterruptedException { 8 JobDetail detail = JobBuilder.newJob(AcceptConcurrentJob.class) 9 .withIdentity("detail", "group0") 10 .build(); 11 12 13 Trigger trigger = TriggerBuilder.newTrigger() 14 .withIdentity("ben_trigger") 15 .usingJobData("name", "ben") 16 .startNow() 17 .build(); 18 19 Trigger triggers = TriggerBuilder.newTrigger() 20 .withIdentity("mike_trigger") 21 .usingJobData("name", "mike") 22 .startNow() 23 .build(); 24 25 26 Scheduler scheduler = new StdSchedulerFactory().getScheduler(); 27 28 scheduler.start(); 29 scheduler.scheduleJob(detail, Sets.newHashSet(trigger,triggers),true); 30 /* 31 * 6 秒钟后关闭 32 */ 33 Thread.sleep(6_000); 34 scheduler.shutdown(); 35 } 36 37 @Data 38 public static class AcceptConcurrentJob implements Job { 39 private String name; 40 41 @Override 42 public void execute(JobExecutionContext context) { 43 try { 44 System.out.printf("i am %s \n", name); 45 Thread.sleep(2_000); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 } 50 } 51 }
2.2 avoid concurrency
In order to avoid concurrency, we can use the annotation @ official of DisallowConcurrentExecution , by the increase in class this comment, we can observe a second trigger was queuing process:
1 /** 2 * @author pancc 3 * @version 1.0 4 */ 5 public class RejectConcurrentDemo { 6 7 public static void main(String[] args) throws SchedulerException, InterruptedException { 8 JobDetail detail = JobBuilder.newJob(RejectConcurrentJob.class) 9 .withIdentity("detail", "group0") 10 .build(); 11 12 13 Trigger trigger = TriggerBuilder.newTrigger() 14 .withIdentity("ben_trigger") 15 .usingJobData("name", "ben") 16 .startNow() 17 .build(); 18 19 Trigger triggers = TriggerBuilder.newTrigger() 20 .withIdentity("mike_trigger") 21 .usingJobData("name", "mike") 22 .forJob("detail", "group0") 23 .startNow() 24 .build(); 25 26 27 Scheduler scheduler = new StdSchedulerFactory().getScheduler(); 28 29 scheduler.start(); 30 scheduler.scheduleJob(detail, trigger); 31 scheduler.scheduleJob(triggers); 32 /* 33 * 6 秒钟后关闭 34 */ 35 Thread.sleep(6_000); 36 scheduler.shutdown(); 37 } 38 39 40 @DisallowConcurrentExecution 41 @Data 42 public static class RejectConcurrentJob implements Job { 43 private String name; 44 45 @Override 46 public void execute(JobExecutionContext context) { 47 try { 48 System.out.printf("i am %s \n", name); 49 Thread.sleep(2_000); 50 } catch (InterruptedException e) { 51 e.printStackTrace(); 52 } 53 } 54 } 55 }
Principle 3 Avoid concurrent exploration
Let's find JobStore implementation class, in this case RAMJobStore, point into the method org.quartz.simpl.RAMJobStore # acquireNextTriggers, you can see a piece of this method:
By presence or absence of the Job class DisallowConcurrentExecution annotation, if present, is rejected concurrently execute method. If you call the Trigger to be executed with a JobDetail objects, the current trigger into the waiting list.
After a Trigger current execution is completed, the waiting list to go back to re-add the Trigger trigger RAMJobStore held by, waiting for a next call happen.