隨着雲平臺,大數據等的出現,用戶或潛在訪問者愈來愈想免費體驗產品,特別是想申請系統(好比開虛擬機,啓動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/