随着云平台,大数据等的出现,用户或潜在访问者愈来愈想免费体验产品,特别是想申请系统(好比开虚拟机,启动docker等),但是系统硬件资源有限,那怎么才能让用户实际操做云平台资源呢,那就免费三天或七天的使用期(也有是一天的使用期),若过时了不花钱续费,系统就要自动清理云环境申请的资源(好多云厂商都是如此,好比某云,免费试用到期了,会发邮件提醒续费),这时就用到了做业调度的功能,这就要用定时器(它能够当作定时的任务,到期会执行,就像闹钟),java生态中有原生的定时器库,也有第三方的好比Quartz,html
下面我就用例子说明下如何使用java
1. java的原生定时器组件Timer和TimerTaskspring
Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,它能够计划执行一个任务一次或反复屡次。docker
Timer类中常见方法api
public class Timer { /** * task queue */ private final TaskQueue queue = new TaskQueue(); /** * The timer thread. */ private final TimerThread thread = new TimerThread(queue); public Timer( ); public Timer(boolean isDaemon); public Timer(String name); public Timer(String name, boolean isDaemon); //在指定延迟后执行指定的任务 public void schedule(TimerTask task, long delay); //在指定的时间执行指定的任务 public void schedule(TimerTask task, Date time); //指定的任务从指定的延迟后开始进行重复的固定延迟执行 public void schedule(TimerTask task, long delay, long period); //指定的任务在指定的时间开始进行重复的固定延迟执行 public void schedule(TimerTask task, Date firstTime, long period); //指定的任务在指定的延迟后开始进行重复的固定速率执行 public void scheduleAtFixedRate(TimerTask task, long delay, long period); //指定的任务在指定的时间开始进行重复的固定速率执行 public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period); //终止计时器,丢弃全部当前已安排的任务 public void cancel( ); //从计时器的任务队列中移除全部已取消的任务 public int purge( ); }
而TimerTask是一个抽象类,实现了Runnable接口,因此也就具有多线程的能力,它的子类表明一个能够被Timer计划的任务。多线程
下面是一个简单的例子,编写任务代码并发
/** * * @author dgm * @describe "测试打印定时器" * @date 2017年4月10日 */ public class PrintTimerTask extends TimerTask { private String name; public PrintTimerTask(String name) { super(); this.name = name; } @Override public void run() { if (System.currentTimeMillis( ) - scheduledExecutionTime( ) > 5000) { return; } System.out.println("线程:"+ name +"***** 在 执行。。"); } }
编写测试代码oracle
/** * * @author dgm * @describe "测试定时器" * @date 2017年4月10日 */ public class TimeTaskTest { public static void main(String[] args) { Timer timer = new Timer(); //设置3秒后启动任务 timer.schedule(new PrintTimerTask("name-0"), 3000); PrintTimerTask secondTask = new PrintTimerTask("name-1"); // 1秒后启动任务,之后每隔3秒执行一次线程 timer.schedule(secondTask, 1000, 3000); Date date = new Date(); // 以date为参数,指定某个时间点执行线程 timer.schedule(new PrintTimerTask("name-3"), new Date( date.getTime() + 5000)); } }
测试结果如图:app
注意此方法:框架
虽然jdk原生能实现须要的定时清理功能,查阅了资料后发现还有更好的方法,接着往下看
2. ScheduledExecutorService(一个接口)
ScheduledExecutorService,是基于线程池设计的定时任务类,每一个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。
一个小例子以下
编写要执行的线程任务
public class PrintScheduledExecutor implements Runnable { private String jobName; public PrintScheduledExecutor(String jobName) { this.jobName = jobName; } @Override public void run() { System.out.println(jobName + " 正在运行中!!!"); } }
测试定时任务执行
public static void main(String[] args) { ScheduledExecutorService service = Executors.newScheduledThreadPool(10); long initialDelay = 1; long period = 1; service.scheduleAtFixedRate(new PrintScheduledExecutor("job1"), initialDelay, period, TimeUnit.SECONDS); service.scheduleWithFixedDelay(new PrintScheduledExecutor("job2"), initialDelay, period, TimeUnit.SECONDS); }
注意scheduleAtFixedRate和scheduleWithFixedDelay:
:
这个scheduledexecutorservice彷佛是不错,我又想有没有更好的调度框架,这时又找到了Quartz(也是我现实中用到的定时调度库)
3. Quartz要登场了,强大无比
简单介绍下,Quarzt是一个项目中定时执行任务的开源项目,Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它能够与J2EE与J2SE应用程序相结合也能够单独使用。它的结构图如图示:
job任务类:表示的任务是什么(好比自定义的任务种类好比清理试用帐号申请的资源),须要定时执行代码的类。
JobDetail:配置任务类的细节,即注入任务类和指定任务类的方法,是一个可执行的工做,它自己多是有状态的。
trigger:即触发器,表明一个调度参数的配置,配置调用的时间,表示什么时候触发job任务。
调度工厂(scheduler):是一个计划调度器容器,容器里面能够盛放众多的JobDetail和trigger,当容器启动后,里面的每一个JobDetail都会根据trigger循序渐进自动去执行。
下面以案例说下如何使用(因为我不是单独使用的,是和spring整合使用)
建立自定义job类(伪代码)
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.context.ApplicationContext; /** * 销毁试用帐号老师的docker集群 * Created by dongguangming on 2017/4/14. */ public class DeTryTeacherAppJob implements Job { public static Logger logger = Logger.getLogger(DeTryTeacherAppJob.class); @Override public void execute(JobExecutionContext jobCtx) throws JobExecutionException { logger.info("[" + currentTime + "]断定失效试用帐号任务开始"); Map datamap = jobCtx.getJobDetail().getJobDataMap(); ApplicationContext ctx = (ApplicationContext) datamap.get("applicationContext"); UserService userService = (UserService) ctx.getBean("userService"); AppService appService = (AppService) ctx.getBean("appService"); // 执行业务操做 ............... } }
而后再spring配置任务类的bean和配置触发器(时间)
<!--销毁试用帐号的docker集群job --> <bean name="deTryTeacherAppJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean" p:jobClass="com.cstor.docker.job.DeTryTeacherAppJob" p:applicationContextJobDataKey="applicationContext"> </bean> <!-- 参考http://www.cnblogs.com/ypf1989/p/5552426.html http://www.cnblogs.com/30go/p/5761917.html --> <bean id="deTryTeacherAppJob" class="org.springframework.scheduling.quartz.CronTriggerBean" p:jobDetail-ref="deTryTeacherAppJobDetail" p:cronExpression="0 0/3 * * * ?" />
最后配置调度工厂而且注入配置好的触发器
<bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="deTryTeacherAppJob"> </list> </property> </bean>
如今能够启动测验了,测试时能够调整触发时间(好比每分钟),下面是con表达式样板
"0 0 12 * * ?" 天天中午12点触发 "0 15 10 ? * *" 天天上午10:15触发 "0 15 10 * * ?" 天天上午10:15触发 "0 15 10 * * ? *" 天天上午10:15触发 "0 15 10 * * ? 2005" 2005年的天天上午10:15触发 "0 * 14 * * ?" 在天天下午2点到下午2:59期间的每1分钟触发 "0 0/5 14 * * ?" 在天天下午2点到下午2:55期间的每5分钟触发 "0 0/5 14,18 * * ?" 在天天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 "0 0-5 14 * * ?" 在天天下午2点到下午2:05期间的每1分钟触发 "0 10,44 14 ? 3 WED" 每一年三月的星期三的下午2:10和2:44触发 "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 "0 15 10 15 * ?" 每个月15日上午10:15触发 "0 15 10 L * ?" 每个月最后一日的上午10:15触发 "0 15 10 ? * 6L" 每个月的最后一个星期五上午10:15触发 "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每个月的最后一个星期五上午10:15触发 "0 15 10 ? * 6#3" 每个月的第三个星期五上午10:15触发
小结:
1. Timer在执行定时任务时只建立一个线程。
2. Timer线程并不捕获异常,TimerTask抛出的未检查的异常会终止timer线程,致使剩下的任务无法执行(这很很差)。
3. ScheduledExecutorService
继承于ExecutorService,
ScheduledThreadPool内部是线程池,支持多个任务并发执行.
4. Quartz很灵活,和spring集成的很好。
d...........
Timer&TimerTask→→→→→ScheduledExecutorService(体系复杂)→→→→→Quartz
参考:
0. Java Timer vs ExecutorService?
1 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html
2. 几种任务调度的 Java 实现方法与比较: https://www.ibm.com/developerworks/cn/java/j-lo-taskschedule/
3. cron表达式生成工具: http://cron.qqe2.com/