Timer
Timer的核心是Timer和TimerTask,Timer負責設定TimerTask的起始與間隔執行時間,使用者需要建立一個timeTask的繼承類,實現run方法,然后將其交給Timer使用即可。
Timer的設計是一個TaskList和一個TaskThread。Timer將接到的任務交給TaskList中,TaskList按照最初執行時間進行排序。TimerThread在創建Timer會成為一個守護線程。這個線程會輪詢任務,找到一個最近要執行的任務,然后休眠,當到達最近要執行任務的開始時間點,TimerThread被喚醒並執行任務。之后Timer更新最近要執行的任務,繼續休眠。
缺點:所有任務都是串行的,前一個任務的延遲或異常會影響到后面的任務。
demo:
1 Timer timer = new Timer(); 2 TimeTask task = new TimeTask(){ 3 public void run(){ 4 //具體業務 5 } 6 }; 7 timer.scheduleAtFixedRate(task, new Date(), 1* 30 * 1000l);
ScheduledExecutor
java 5退出了ScheduledExecutor框架, 與timer不同的是,任務執行時是並發的,相互之間不受干擾,(保留意見,不一定非得是並發執行的就好),只有當任務執行時間到的時候,scheduedExecutor才會真正的執行線程,其余時間ScheduledExecutor都在輪詢任務的狀態。
1 public class ScleduledExecutorTest implements Runnable{ 2 private String jobName = ""; 3 public ScheduledExecutorTest(String jobName){ 4 super(); 5 this.jobName = jobName; 6 } 7 public void run(){ 8 System.out.println("execute " + jobName); 9 } 10 public static void main(String[] args){ 11 ScheduledExecutorService service = Executors.newScheduledThreadPool(10); 12 service.scheduleAtFixRate(new ScheduledExecutorTest("job1"),1,1,TimeUnit.SECONDS); 13 service.scheduleWithFixedDelay( 14 new ScheduledExecutorTest("job2"), 1, 15 1, TimeUnit.SECONDS); 16 } 17 }
注意,能做定時執行的只能是newScheduledThreadPool 這個是專門做調度任務的,其他的線程池特性如下:
newSingleThreadExecutor:創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
newFixedThreadPool:創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。
newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那么就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
newScheduledThreadPool:創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。
newSingleThreadExecutor:創建一個單線程的線程池。此線程池支持定時以及周期性執行任務的需求。
並且線程池在停止的時候所用的shutdown與shutdownNow是不一樣的,shutdown會繼續執行並且完成所有未執行的任務,shutdownNow 會清楚所有未執行的任務並且在運行線程上調用interrupt() 。然而interrupt僅僅是在當前線程中打一個停止的標記。還需要加一個判斷才能對線程的停止。
Thread.java提供了兩個方法:
this.interrupted();
this.isInterrupted();
官方幫助文檔對interrupted方法的解釋是:
調用后,線程的中斷狀態被清除,也就是如果線程中斷了,第一次返回true,第二次返回false。 並且這是一個靜態方法。
isInterrupted不是一個靜態方法,也不會清除中斷狀態。
中斷線程通過標記判斷,要么拋出異常,要么直接return。
ScheduledExecutor可以與Calendar實現更復雜的調度,比如制定每周的某個時間段執行。
也就是在執行線程中加上了Calandar的判斷
Calendar ca = Calendar.getInstance(); int h = ca.get(Calendar.HOUR_OF_DAY); int mi = h*60+ca.get(Calendar.MINUTE); int ss = ca.get(Calendar.SECOND);
通過if進行判斷,要注意,調度器的喚醒時間最好是各個線程激活的最大公約數。
Quartz
需要更精准的調度,需要使用Quartz框架了。
public static void main(String args[]){ UpdateJob.task(AutoJob.class,"0/1 * * * * ?" ); }
public class UpdateClientTimer { public static void task(Class cls, String expression) throws SchedulerException { SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); long ctime = System.currentTimeMillis(); JobDetail jobDetail = new JobDetail("detail", "group", cls); CronTrigger cronTrigger = new CronTrigger("cronTrigger", "group"); try { CronExpression cexp = new CronExpression(expression); cronTrigger.setCronExpression(cexp); } catch (Exception e) { e.printStackTrace(); } scheduler.scheduleJob(jobDetail, cronTrigger); scheduler.start(); } }
調度周期是 "0/1 * * * * ?" 更精准的調度需要自行配置。
UpdateClientTimer 大體與官方文檔相當。真正執行業務在本例的Autojob中。
public class AutoJob implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException { Calendar ca = Calendar.getInstance(); int h = ca.get(Calendar.HOUR_OF_DAY); int mi = h*60+ca.get(Calendar.MINUTE); int ss = ca.get(Calendar.SECOND); if(過濾條件){ } }
Quartz的核心類包括Scheduler,job以及Trigger。其中job負責定義需要執行的任務,trigger負責設置調度策略,Scheduler 將二者組裝在一起。
有相關的官方說明文檔,可以直接拿demo用。