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


    1 前言

    根据 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 
    ", 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 方法

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

     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 
    ", 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 
    ", name);
    49                 Thread.sleep(2_000);
    50             } catch (InterruptedException e) {
    51                 e.printStackTrace();
    52             }
    53         }
    54     }
    55 }

    3 避免并发的原理探索

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

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

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

  • 相关阅读:
    [转]Native进程的运行过程
    android ARM 汇编学习 —— hello world
    android ARM 汇编学习—— 在 android 设备上编译c/cpp代码并用objdump/readelf等工具分析
    Linux buffer/cache异同
    分布式消息队列RocketMQ与Kafka架构上的巨大差异之1 -- 为什么RocketMQ要去除ZK依赖?
    kafka对比RocketMQ(转)
    Jmeter测试webocket协议
    Linux atop监控工具部署
    MySql计算两个日期的时间差函数
    Python selenium —— 一定要会用selenium的等待,三种等待方式解读(转)
  • 原文地址:https://www.cnblogs.com/siweipancc/p/12596241.html
Copyright © 2020-2023  润新知