根据 Quartz 的设计,一个 Job 可以绑定多个 Trigger,必然会遇到并发的问题。

2 并发

2.1 复现

让我们编写一个并发的例子:

 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 }

 

请注意上边的 Details 的 Identity ,设置为 group0.detail,同时我们创建了两个 Trigger,第二个 trigger 在创建的时候通过指定 Identity 绑定到了目标 Job,接着提交这个 Job,与两个 Trigger ,可以看到两个触发器同时出发了 Job 的 execute 方法

Java 定时任务 Quartz (三)—— 并发

 

上边的代码也可以简化为以下形式:

 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 避免并发

为了避免并发,我们可以使用官方提供的注解 @DisallowConcurrentExecution,通过在 类上增加这个注解,我们可以观察到第二个 trigger 进行了排队处理:

 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 }

Java 定时任务 Quartz (三)—— 并发

 

 

3 避免并发的原理探索

让我们找到 JobStore 的实现类,在这里是 RAMJobStore,点进去方法 org.quartz.simpl.RAMJobStore#acquireNextTriggers,可以看到这个方法的某个块:

Java 定时任务 Quartz (三)—— 并发

 

 通过对 Job 类上的是否存在  DisallowConcurrentExecution 注解,如果存在,表示拒绝并发执行 execute 方法。如果与即将执行的 Trigger 调用同一个 JobDetail 对象,则将当前 trigger 放入等待列表。

Java 定时任务 Quartz (三)—— 并发

 

之后当前一个 Trigger 执行完毕,将等待中的 Trigger 重新加回去 RAMJobStore 持有的 trigger 列表中,等待下一次调用发生。

相关文章:

  • 2022-02-02
  • 2021-07-09
  • 2022-01-06
  • 2022-01-19
  • 2021-10-11
猜你喜欢
  • 2022-12-23
  • 2021-11-27
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-07-28
  • 2021-04-30
相关资源
相似解决方案