JAVA 并发编程-基于线程池设计的ScheduledExecutor(八)
发布日期:2022-01-11 03:09:54 浏览次数:3 分类:技术文章

本文共 4981 字,大约阅读时间需要 16 分钟。

上篇博客《 并发编程-线程池(七)》中曾介绍到newScheduledThreadPool(intcorePoolSize),创建corePoolSize大小的线程池。此线程池支持定时以及周期性执行任务的需求。

    接下来我们一起来分析一下Java中几种任务调度实现与比较

    任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。

 

Timer

相信大家都已经非常熟悉java.util.Timer 了,它是最简单的一种实现任务调度的方法,下面给出一个具体的例子:

[java]
  1. package com.tgb.hjy;  
  2.   
  3. import java.util.Timer;  
  4. import java.util.TimerTask;  
  5.   
  6. public class TimerTest extends TimerTask {   
  7.   
  8.      private String jobName = "";   
  9.   
  10.      public TimerTest(String jobName) {   
  11.          super();   
  12.          this.jobName = jobName;   
  13.      }   
  14.   
  15.      @Override   
  16.      public void run() {   
  17.          System.out.println("execute " + jobName);   
  18.      }   
  19.   
  20.      public static void main(String[] args) {   
  21.          Timer timer = new Timer();   
  22.          long delay1 = 1 * 1000;   
  23.          long period1 = 1000;   
  24.          // 从现在开始 1 秒钟之后,每隔 1 秒钟执行一次 job1   
  25.          timer.schedule(new TimerTest("job1"), delay1, period1);   
  26.          long delay2 = 2 * 1000;   
  27.          long period2 = 2000;   
  28.          // 从现在开始 2 秒钟之后,每隔 2 秒钟执行一次 job2   
  29.          timer.schedule(new TimerTest("job2"), delay2, period2);   
  30.         }   
  31.      }   

输出结果:

 

execute job1

execute job1

execute job2

execute job1

execute job1

execute job2

execute job1

execute job1

execute job2

 

    使用 Timer 实现任务调度的核心类是Timer 和 TimerTask。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。使用者只需要创建一个 TimerTask的继承类,实现自己的 run 方法,然后将其丢给 Timer 去执行即可。

 

    Timer 的设计核心是一个 TaskList和一个 TaskThread。Timer 将接收到的任务丢到自己的 TaskList 中,TaskList 按照 Task的最初执行时间进行排序。TimerThread 在创建 Timer时会启动成为一个守护线程。这个线程会轮询所有任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread被唤醒并执行该任务。之后 TimerThread 更新最近一个要执行的任务,继续休眠。

 

    Timer的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务。

 

ScheduledExecutor

 

    鉴于 Timer 的上述缺陷,Java 5推出了基于线程池设计的ScheduledExecutor。其设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。

[java]
  1. package com.tgb.hjy;  
  2.   
  3. import java.util.concurrent.Executors;  
  4. import java.util.concurrent.ScheduledExecutorService;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. public class ScheduledExecutorTest implements Runnable {  
  8.     private String jobName = "";  
  9.   
  10.     public ScheduledExecutorTest(String jobName) {  
  11.         super();  
  12.         this.jobName = jobName;  
  13.     }  
  14.   
  15.     @Override  
  16.     public void run() {  
  17.         System.out.println("execute " + jobName);  
  18.     }  
  19.   
  20.     public static void main(String[] args) {  
  21.         ScheduledExecutorService service = Executors.newScheduledThreadPool(10);  
  22.   
  23.         long initialDelay1 = 1;  
  24.         long period1 = 1;  
  25.         // 从现在开始1秒钟之后,每隔1秒钟执行一次job1 scheduleAtFixedRate  
  26.         //每次执行时间为上一次任务开始起向后推一个时间间隔  
  27.         //已固定的频率来执行某项计划(任务).固定的频率来执行某项计划,它不受计划执行时间的影响。到时间,它就执行。  
  28.         service.scheduleAtFixedRate(  
  29.                 new ScheduledExecutorTest("job1"), initialDelay1,  
  30.                 period1, TimeUnit.SECONDS);  
  31.   
  32.         long initialDelay2 = 1;  
  33.         long delay2 = 1;  
  34.         // 从现在开始2秒钟之后,每隔2秒钟执行一次job2 scheduleWithFixedDelay  
  35.         //每次执行时间为上一次任务结束起向后推一个时间间隔  
  36.         //相对固定的延迟后,执行某项计划.即无论某个任务执行多长时间,等执行完了,我再延迟指定的时间。它受计划执行时间的影响。  
  37.         service.scheduleWithFixedDelay(  
  38.                 new ScheduledExecutorTest("job2"), initialDelay2,  
  39.                 delay2, TimeUnit.SECONDS);  
  40.     }  
  41. }  

执行结果:

 

execute job1

execute job2

execute job1

execute job2

execute job1

execute job2

 

    ScheduledExecutorService中两种最常用的调度方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。ScheduleAtFixedRate每次执行时间为上一次任务开始起向后推一个时间间隔;ScheduleWithFixedDelay每次执行时间为上一次任务结束起向后推一个时间间隔。由此可见,ScheduleAtFixedRate是基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度。

 

用 ScheduledExecutor 和Calendar 实现复杂任务调度

 

    Timer 和 ScheduledExecutor都仅能提供基于开始时间与重复间隔的任务调度,不能胜任更加复杂的调度需求。比如,设置每星期二的 16:38:10 执行任务。该功能使用 Timer 和ScheduledExecutor 都不能直接实现,但我们可以借助 Calendar 间接实现该功能。

 

    注,上述方法实现该任务调度比较麻烦,这就需要一个更加完善的任务调度框架来解决这些复杂的调度问题。幸运的是,开源工具包Quartz提供了这方面强大的支持。

 

Quartz

 

Quartz可以满足更多更复杂的调度需求,首先让我们看看如何用 Quartz 实现每星期二 16:38 的调度安排:

[java]
  1. package com.tgb.hjy;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.quartz.Job;  
  6. import org.quartz.JobDetail;  
  7. import org.quartz.JobExecutionContext;  
  8. import org.quartz.JobExecutionException;  
  9. import org.quartz.Scheduler;  
  10. import org.quartz.SchedulerFactory;  
  11. import org.quartz.Trigger;  
  12. import org.quartz.helpers.TriggerUtils;  
  13.   
  14. public class QuartzTest implements Job {  
  15.   
  16.     @Override  
  17.     //该方法实现需要执行的任务  
  18.     public void execute(JobExecutionContext arg0) throws JobExecutionException {  
  19.         System.out.println("Generating report - "  
  20.                 + arg0.getJobDetail().getFullName() + ", type ="  
  21.                 + arg0.getJobDetail().getJobDataMap().get("type"));  
  22.         System.out.println(new Date().toString());  
  23.     }  
  24.     public static void main(String[] args) {  
  25.         try {  
  26.             // 创建一个Scheduler  
  27.             SchedulerFactory schedFact =   
  28.             new org.quartz.impl.StdSchedulerFactory();  
  29.             Scheduler sched = schedFact.getScheduler();  
  30.             sched.start();  
  31.             // 创建一个JobDetail,指明name,groupname,以及具体的Job类名,  
  32.             //该Job负责定义需要执行任务  
  33.             JobDetail jobDetail = new JobDetail("myJob""myJobGroup",  
  34.                     QuartzTest.class);  
  35.             jobDetail.getJobDataMap().put("type""FULL");  
  36.             // 创建一个每周触发的Trigger,指明星期几几点几分执行  
  37.             Trigger trigger = TriggerUtils.makeWeeklyTrigger(31638);  
  38.             trigger.setGroup("myTriggerGroup");  
  39.             // 从当前时间的下一秒开始执行  
  40.             trigger.setStartTime(TriggerUtils.getEvenSecondDate(new Date()));  
  41.             // 指明trigger的name  
  42.             trigger.setName("myTrigger");  
  43.             // 用scheduler将JobDetail与Trigger关联在一起,开始调度任务  
  44.             sched.scheduleJob(jobDetail, trigger);  
  45.   
  46.         } catch (Exception e) {  
  47.             e.printStackTrace();  
  48.         }  
  49.     }  
  50. }   

    使用Quartz我们非常简洁地实现了一个上述复杂的任务调度。Quartz设计的核心类包括 Scheduler, Job 以及 Trigger。其中,Job 负责定义需要执行的任务,Trigger负责设置调度策略,Scheduler 将二者组装在一起,并触发任务开始执行。

转载地址:https://yuki-ho.blog.csdn.net/article/details/52316357 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:JAVA 并发编程-返回执行结果(Callable和Future)(九)
下一篇:JAVA 并发编程-线程池(七)

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年03月26日 08时19分21秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

rn webview加载本地静态html,React Native - Webview 加载本地文件 2019-04-21
dax powerbi 生成表函数_Power BI |DAX函数のCALCULATETABLE、CALENDAR函数以及相关表生成函数... 2019-04-21
编程之类的文案_如何锻炼写文案的能力? 2019-04-21
vscode 不能使用中文输入法_vscode中vim插件设置 2019-04-21
当集合a为空集时a的取值范围_1.1.2 集合间的基本关系 2019-04-21
vue 可合并表格组件_Vue实战046:详解Mixins混入使用和注意事项 2019-04-21
python包怎么做双重差分did分析_多变量相关性分析(一个因变量与多个自变量) 2019-04-21
fi sap 凭证冲销 稅_SAP中的成本要素 2019-04-21
mysql幻读是什么意思_MySQL中的幻读,你真的理解吗? 2019-04-21
mysql执行计划中性能最差的是_MySQL性能优化(七):MySQL执行计划,真的很重要,来一起学习吧... 2019-04-21
易语言执行mysql命令_易语言通过“打开”命令操作数据库 2019-04-21
mysql slave 1062_mysql主从同步slave错误1062 2019-04-21
mysql构造器_MySQL行构造器表达式优化(Row Constructor Expression) 2019-04-21
2008日志清理 server sql_SQL Server 2008 清除日志 2019-04-21
mac mysql root 权限_Mac平台重新设置MySQL的root密码 2019-04-21
mysql新增一列_MySQL-ProxySQL中间件 2019-04-21
mysql 30入门_30分钟带你快速入门MySQL教程 2019-04-21
kangle主机怎么配置MySQL_kangle web服务+easypanel主机控制面板快速搭建网站和数据库以及管理空间详细教程... 2019-04-21
mysql 翻页 存储过程_MySQl通用翻页(存储过程) 2019-04-21
2020word替换所有文本_Excel字符函数(5):REPLACE、SUBSTITUTE查找替换函数之区别... 2019-04-21