就以鬧鐘的例子開頭吧(後續小節皆以鬧鐘爲例,全部源代碼只列關鍵部分)。java
public class ScheduleDemo { public static void main(String[] args) throws InterruptedException { long delay = 1000; // 一秒後開始執行 long period = 2000; // 執行間隔 Timer timer = new Timer(); AlarmTask alarm = new AlarmTask("鬧鐘1"); log.info("["+Thread.currentThread().getName()+"]開啓鬧鐘調度!"); timer.schedule(alarm,delay,period); } /** * 模擬鬧鐘 */ static class AlarmTask extends TimerTask{ String name ; public AlarmTask(String name){ this.name=name; } @Override public void run() { log.info("["+Thread.currentThread().getName()+"]"+name+":嘀。。。"); Thread.sleep(1000); //模擬鬧鐘執行時間,省略異常。。。 } } }
一秒之後鬧鐘每隔兩秒執行一次。ide
[main] 開啓鬧鐘調度! [Timer-0] 鬧鐘1:嘀。。。 [Timer-0] 鬧鐘1:嘀。。。 [Timer-0] 鬧鐘1:嘀。。。
從打印結果能夠看到,鬧鐘調度與執行並不是一線程。oop
下面是Timer時序圖,能夠了解Timer的大概流程。源碼分析
下面開始分析Timer源碼。this
public class Timer { private final TaskQueue queue = new TaskQueue(); private final TimerThread thread = new TimerThread(queue); public Timer() { this("Timer-" + serialNumber()); } public Timer(String name) { thread.setName(name); thread.start(); }
能夠看到,Timer中維護了一個內部線程與隊列,且在實例化Timer的同時,就已初始化好了。在初始化Timer時,內部線程TimerThread開始啓動,下面是TimerThread的執行過程。spa
class TimerThread extends Thread { public void run() { mainLoop(); } private void mainLoop() { while (true) { synchronized(queue) { // 隊列爲空,線程被阻塞 while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait();
能夠看到,雖然線程TimerThread已啓動,但因隊列爲空,線程被阻塞(等待queue鎖)。線程
以上是Timer timer = new Timer()的整個運行過程,繼續看timer.schedule(alarm,delay,period)。code
public class Timer { public void schedule(TimerTask task, long delay, long period) { sched(task, System.currentTimeMillis()+delay, -period); } private void sched(TimerTask task, long time, long period) { synchronized(queue) { synchronized(task.lock) { task.nextExecutionTime = time; task.period = period; task.state = TimerTask.SCHEDULED; } // 將鬧鐘加入隊列 queue.add(task); // 此時正好知足條件,主線程釋放queue鎖,並喚醒TimerThread if (queue.getMin() == task) queue.notify(); } }
從源碼可看出,主線程正好知足queue.getMin() == task,此時將喚醒TimerThread線程(waiting)並釋放queue鎖。隊列
下面再切換到TimerThread的運行場景。get
private void mainLoop() { while (true) { synchronized(queue) { while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait(); if (queue.isEmpty()) break; task = queue.getMin(); synchronized(task.lock) { currentTime = System.currentTimeMillis(); executionTime = task.nextExecutionTime; // 已經到了執行時間 if (taskFired = (executionTime<=currentTime)) { // ...從新定義下次鬧鐘執行時間 } } if (!taskFired) // 執行時間未到,線程再次阻塞 queue.wait(executionTime - currentTime); } if (taskFired) task.run(); // 同步執行用戶定義的鬧鐘 } }
經過上面的源碼分析,TimerThread被喚醒後,將判斷執行時間,時間到則初始化下次鬧鐘的執行時間並運行本次鬧鐘,不然線程將等待指定時間。
如此周而復始。。