最近需要用到定時調用的功能。可以通過java的Timer類來進行定時調用,下面是有關Timer的一些相關知識。
其實就Timer來講就是一個調度器,而TimerTask呢只是一個實現了run方法的一個類,而具體的TimerTask需要由你自己來實現,例如這樣:
Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() { System.out.println("11232"); } }, 200000 , 1000);
在說到timer的原理時,我們先看看Timer里面的一些常見方法:
1、這個方法是調度一個task,經過delay(ms)后開始進行調度,僅僅調度一次。
public void schedule(TimerTask task, long delay)
2、在指定的時間點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的結束。
public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }
另外兩個構造方法負責傳入名稱和將timer啟動:
public Timer(String name, boolean isDaemon) { thread.setName(name); thread.setDaemon(isDaemon); thread.start(); }
這里有一個thread,這個thread很明顯是一個線程,被包裝在了Timer類中,我們看下這個thread的定義是:
private TimerThread thread = new TimerThread(queue);
而定義TimerThread部分的是:
看到這里知道了,Timer內部包裝了一個線程,用來做獨立於外部線程的調度,而TimerThread是一個default類型的,默認情況下是引用不到的,是被Timer自己所使用的。
private TaskQueue queue = new TaskQueue();
看名字就知道是一個隊列,隊列里面可以先猜猜看是什么,那么大概應該是我要調度的任務吧,先記錄下了,接下來繼續向下看:
來看下方法:
public void schedule(TimerTask task, long delay)
的源碼如下:
public void schedule(TimerTask task, long delay) { if (delay < 0) throw new IllegalArgumentException("Negative delay."); sched(task, System.currentTimeMillis()+delay, 0); }
這里調用了另一個方法,將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); } }
2、通過匿名內部類實現
Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { public void run() { System.out.println("abc"); } }, 1000 , 1000);
原文地址:https://www.cnblogs.com/0201zcr/p/4703061.html
另,關於timer基本的demo使用,可以參考我的github,上面有專門的一個項目講解,地址:https://github.com/liuzhihu/TimedTask1.git