最近須要用到定時調用的功能。能夠經過java的Timer類來進行定時調用,下面是有關Timer的一些相關知識。java
其實就Timer來說就是一個調度器,而TimerTask呢只是一個實現了run方法的一個類,而具體的TimerTask須要由你本身來實現,例如這樣:api
Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() { System.out.println("11232"); } }, 200000 , 1000);
在說到timer的原理時,咱們先看看Timer裏面的一些常見方法:數組
一、這個方法是調度一個task,通過delay(ms)後開始進行調度,僅僅調度一次。安全
public void schedule(TimerTask task, long delay)
二、在指定的時間點time上調度一次。數據結構
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 Timer() { this("Timer-" + serialNumber()); }
建立的線程不爲主線程,則主線程結束後,timer自動結束,而無需使用cancel來完成對timer的結束。ide
public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }
另外兩個構造方法負責傳入名稱和將timer啓動:ui
public Timer(String name, boolean isDaemon) { thread.setName(name); thread.setDaemon(isDaemon); thread.start(); }
這裏有一個thread,這個thread很明顯是一個線程,被包裝在了Timer類中,咱們看下這個thread的定義是:this
private TimerThread thread = new TimerThread(queue);
而定義TimerThread部分的是:
看到這裏知道了,Timer內部包裝了一個線程,用來作獨立於外部線程的調度,而TimerThread是一個default類型的,默認狀況下是引用不到的,是被Timer本身所使用的。spa
private TaskQueue queue = new TaskQueue();
看名字就知道是一個隊列,隊列裏面能夠先猜猜看是什麼,那麼大概應該是我要調度的任務吧,先記錄下了,接下來繼續向下看:線程
來看下方法:
public void schedule(TimerTask task, long delay)
的源碼以下:
1 public void schedule(TimerTask task, long delay) { 2 if (delay < 0) 3 throw new IllegalArgumentException("Negative delay."); 4 sched(task, System.currentTimeMillis()+delay, 0); 5 }
這裏調用了另外一個方法,將task傳入,第一個參數傳入System.currentTimeMillis()+delay可見爲第一次須要執行的時間的 時間點了(若是傳入Date,就是對象.getTime()便可,因此傳入Date的幾個方法就不用多說了),而第三個參數傳入了0,這裏能夠猜下要麼是 時間片,要麼是次數啥的,不過等會就知道是什麼了;另外關於方法:sched的內容咱們不着急去看他,先看下重載的方法中是如何作的
public void schedule(TimerTask task, long delay,long period)
源碼爲:
public void schedule(TimerTask task, long delay, long period) { if (delay < 0) throw new IllegalArgumentException("Negative delay."); if (period <= 0) throw new IllegalArgumentException("Non-positive period."); sched(task, System.currentTimeMillis()+delay, -period); }
看來也調用了方法sched來完成調度,和上面的方法惟一的調度時候的區別是增長了傳入的period,而第一個傳入的是0,因此肯定這個參數爲時間片, 而不是次數,注意這個裏的period加了一個負數,也就是取反,也就是咱們開始傳入1000,在調用sched的時候會變成-1000,其實最終閱讀完 源碼後你會發現這個算是老外對於一種數字的理解,而並不是有什麼特殊的意義,因此閱讀源碼的時候也有這些困難所在。
public void scheduleAtFixedRate(TimerTasktask,long delay,long period)
源碼爲:
public void scheduleAtFixedRate(TimerTask task, long delay, long period) { if (delay < 0) throw new IllegalArgumentException("Negative delay."); if (period <= 0) throw new IllegalArgumentException("Non-positive period."); sched(task, System.currentTimeMillis()+delay, period); }
惟一的區別就是在period沒有取反,其實你最終閱讀完源碼,上面的取反沒有什麼特殊的意義,老外不想增長一個參數來表示 scheduleAtFixedRate,而scheduleAtFixedRate和schedule的大部分邏輯代碼一致,所以用了參數的範圍來做爲 區分方法,也就是當你傳入的參數不是正數的時候,你調用schedule方法正好是獲得scheduleAtFixedRate的功能,而調用 scheduleAtFixedRate方法的時候獲得的正好是schedule方法的功能,呵呵,這些討論沒什麼意義,討論實質和重點:
來看sched方法的實現體:
private void sched(TimerTask task, long time, long period) { if (time < 0) throw new IllegalArgumentException("Illegal execution time."); synchronized(queue) { if (!thread.newTasksMayBeScheduled) throw new IllegalStateException("Timer already cancelled."); synchronized(task.lock) { if (task.state != TimerTask.VIRGIN) throw new IllegalStateException( "Task already scheduled or cancelled"); task.nextExecutionTime = time; task.period = period; task.state = TimerTask.SCHEDULED; } queue.add(task); if (queue.getMin() == task) queue.notify(); } }
queue爲一個隊列,咱們先不看他數據結構,看到他在作這個操做的時候,發生了同步,因此在timer級別,這個是線程安全的,最後將task相關的參數賦值,主要包含nextExecutionTime(下一次執行時間),period(時間片),state(狀態),而後將它放入queue隊列中,作一次notify操做,爲何要作notify操做呢?看了後面的代碼你就知道了。
簡言之,這裏就是講task放入隊列queue的過程,此時,你可能對queue的結構有些興趣,那麼咱們先來看看queue屬性的結構TaskQueue:
class TaskQueue { private TimerTask[] queue = new TimerTask[128]; private int size = 0;
可見,TaskQueue的結構很簡單,爲一個數組,加一個size,有點像ArrayList,是否是長度就128呢,固然不 是,ArrayList能夠擴容,它能夠,只是會形成內存拷貝而已,因此一個Timer來說,只要內部的task個數不超過128是不會形成擴容的;內部 提供了add(TimerTask)、size()、getMin()、get(int)、removeMin()、quickRemove(int)、 rescheduleMin(long newTime)、isEmpty()、clear()、fixUp()、fixDown()、heapify();
public class MyTask extends TimerTask { @Override public void run() { SimpleDateFormat sdf = null; sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); System.out.println("當前時間:" + sdf.format(new Date())); } }
public class TestTask { public static void main(String[] args) { Timer t = new Timer(); // 創建Timer對象 MyTask task = new MyTask(); //定義任務 t.schedule(task, 1000,2000);//設置任務的執行,1秒後開始,每2秒執行一次 Calendar cal = Calendar.getInstance(); cal.set(Calendar.MINUTE, 30); t.schedule(task, cal.getTime() , 2000); } }
二、經過匿名內部類實現
Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { public void run() { System.out.println("abc"); } }, 1000 , 1000);
致謝:感謝您的耐心閱讀!