最近項目中好多地方都須要用到定時器,一開始用的是netty的hashWheel,後來發現刪除任務的時候不是很好刪除,因而就放棄了,而後選擇了Quartz。html
1)Quartz將定時任務分爲任務和觸發器,而hashWheel只有任務的概念spring
2)Quartz經過一個TreeSet對全部的觸發器進行管理,而hashWheel經過一個hash輪來對全部的任務進行管理數據庫
3)Quartzl可以很是方便的刪除定時任務,而netty的hashWheel暫時沒有刪除任務的接口(除非本身實現一個hashWheel定時器)app
4)Quartz有一個專門的調度線程對任務進行管理,任務執行有另外專門的線程池,而hashWheel用一個線程實現對任務的管理和任務的執行。函數
5)Quartz可以經過序列化,將定時任務保存在數據庫,而hashWheel不能post
總的來講,Quartz的功能相對強大,而hashWheel相對要輕量級一點。spa
接下來就講講Quartz的原理。線程
1)首先任務調度器調度的時序大體以下所示:3d
在這裏將幾個重要的類調用的過程以序列圖的形式展示出來,上半部分展示的是啓動過程,下半部分展示的是任務調度的過程。netty
步驟1.用戶首先須要生成一個調度器工廠SchedulerFactory,能夠用下面的方式實現本身的定製化:
1 Properties properties=new Properties(); properties.put("org.quartz.threadPool.class","org.quartz.simpl.SimpleThreadPool"); 2 properties.put("org.quartz.threadPool.threadCount","10"); 3 SchedulerFactory sf=new StdSchedulerFactory(properties);
步驟2.而後經過getScheduler()方法從調度器工廠裏獲得調度器實例,首先查找有沒有這樣的調度器,沒有的話,就生成一個,有的話直接返回。因此獲得的通常是單例,即默認的調度器。
步驟3.Scheduler有一個QuartzSchedulerThread(Thread的子類)屬性,在scheduler實例化的時候,實例化了一個對象,並用ThreadExecutor啓動該線程對象。該線程就是調度線程,主要任務就是不停的從JobStore中獲取即將被觸發的觸發器(默認30s調度一次)。在這個時候調度線程雖然啓動,可是處於pause狀態。
步驟4.接下來是任務調度的部分:
1 Scheduler scheduler=sf.getScheduler(); 2 scheduler.addJobListener(new TaskListener()); 3 scheduler.scheduleJob(jobDetail, simpleTrigger); 4 scheduler.start();
client經過scheduleJob()方法將任務和觸發器存儲在JobStore中,經過start()方法將QuartzSchedulerThread的pause狀態設爲false,通知調度線程執行任務,此後調度線程不停的從JobStore中去取即將觸發的任務。
2)任務執行的時序以下所示:
上半部分展示的是任務執行以前準備工做的時序,下半部分展示的是任務執行的時序。
步驟1.調度線程首先去線程池中獲取可用的線程,若是沒有的話,就阻塞。
步驟2.從JobStore(從存儲介質中獲取觸發器,存儲介質能夠是內存也能夠是數據庫)獲取(接下來30s內的)觸發器,而後等待該觸發器觸發。
步驟3.調度線程建立一個JobRunShell(就是一個Runnable),而後從線程池中調用線程執行該任務。
接下來就是任務執行的時序:
步驟4.獲取trigger、JobDetail以及生成Job實例,而後執行job的execute接口函數。
3)持久化的任務的執行時序以下:
以上就是Quartz的基本工做流程。
我在使用的時候遇到的一些問題:
1.Quartz與Spring的整合-Quartz中的job如何自動注入spring容器託管的對象?
這個問題網上已經有解決方法,可是按照它的步驟執行以後仍是不行,後來通過嘗試發現,在實現接口ApplicationContextAware的時候,須要將private ApplicationContext applicationContext;改爲靜態的private static ApplicationContext applicationContext,
以後這個問題就獲得完美解決了。