1. 程式人生 > 實用技巧 >Java 定時調配 Timer 類和定任務 TimerTask 類

Java 定時調配 Timer 類和定任務 TimerTask 類

前言

在我們日常生活中,我們常常會遇到有關計時器的事情。如商城類專案會在某年某月某日某時某分某秒進行特價活動,那麼當時間到達這個時間點上的時候該事件就會觸發。

1、Timer 類建構函式摘要

1 Timer()
2           建立一個新計時器。
3 Timer(boolean isDaemon)
4           建立一個新計時器,可以指定其相關的執行緒作為守護執行緒執行。
5 Timer(String name)
6           建立一個新計時器,其相關的執行緒具有指定的名稱
7 Timer(String name, boolean isDaemon)
8           建立一個新計時器,其相關的執行緒具有指定的名稱,並且可以指定作為守護執行緒執行

2、Timer 類方法摘要

 1 void cancel()   
 2           終止此計時器,丟棄所有當前已安排的任務。   
 3 int purge()   
 4           從此計時器的任務佇列中移除所有已取消的任務。   
 5 void schedule(TimerTask task, Date time)   
 6           安排在指定的時間執行指定的任務。   
 7 void schedule(TimerTask task, Date firstTime, long period)   
 8           安排指定的任務在指定的時間開始進行重複的固定延遲執行。   
9 void schedule(TimerTask task, long delay) 10 安排在指定延遲後執行指定的任務。 11 void schedule(TimerTask task, long delay, long period) 12 安排指定的任務從指定的延遲後開始進行重複的固定延遲執行。 13 void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 14 安排指定的任務在指定的時間開始進行重複的固定速率執行。
15 void scheduleAtFixedRate(TimerTask task, long delay, long period) 16 安排指定的任務在指定的延遲後開始進行重複的固定速率執行。

3、TimerTask 類方法摘要

1 boolean cancel()   
2           取消此計時器任務。   
3 void run()   
4           此計時器任務要執行的操作。   
5 long scheduledExecutionTime()   
6           返回此任務最近實際 執行的已安排 執行時間。

4、Timer 原始碼分析

  1 package java.util;
  2 import dalvik.annotation.optimization.ReachabilitySensitive;
  3 import java.util.Date;
  4 import java.util.concurrent.atomic.AtomicInteger;
  5 
  6 
  7 // 定時器類
  8 public class Timer {
  9 
 10     // 任務佇列
 11     @ReachabilitySensitive
 12     private final TaskQueue queue = new TaskQueue();
 13 
 14     // 內建執行緒
 15     @ReachabilitySensitive
 16     private final TimerThread thread = new TimerThread(queue);
 17 
 18     /*
 19         只是重寫了 finalize 方法而已,是為了垃圾回收的時候,將相應的資訊回收掉,做 GC 的回補,
 20         也就是當 timer 執行緒由於某種原因死掉了,而未被 cancel,裡面的佇列中的資訊需要清空掉,
 21         不過我們通常是不會考慮這個方法的,所以知道 java 寫這個方法是幹什麼的就行了。
 22     */
 23     private final Object threadReaper = new Object() {
 24         protected void finalize() throws Throwable {
 25             synchronized(queue) {
 26                 thread.newTasksMayBeScheduled = false;
 27                 queue.notify();
 28             }
 29         }
 30     };
 31 
 32     private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
 33     private static int serialNumber() {
 34         return nextSerialNumber.getAndIncrement();
 35     }
 36 
 37     // 空的建構函式,這裡會呼叫 Timer(String name)  給相應執行緒設定預設名稱
 38     public Timer() {
 39         this("Timer-" + serialNumber());
 40     }
 41 
 42     // 是否設定為守護執行緒並設定預設名稱
 43     public Timer(boolean isDaemon) {
 44         this("Timer-" + serialNumber(), isDaemon);
 45     }
 46 
 47     // 給相應執行緒設定指定名稱並開啟執行緒
 48     public Timer(String name) {
 49         thread.setName(name);
 50         thread.start();
 51     }
 52 
 53     // 設定指定名稱以及是否設定為守護執行緒並開啟執行緒
 54     public Timer(String name, boolean isDaemon) {
 55         thread.setName(name);
 56         thread.setDaemon(isDaemon);
 57         thread.start();
 58     }
 59 
 60     // 調配任務,設定任務多久後開始
 61     public void schedule(TimerTask task, long delay) {
 62         if (delay < 0)
 63             throw new IllegalArgumentException("Negative delay.");
 64         sched(task, System.currentTimeMillis()+delay, 0);
 65     }
 66 
 67     // 調配任務,設定任務什麼時候開始
 68     public void schedule(TimerTask task, Date time) {
 69         sched(task, time.getTime(), 0);
 70     }
 71 
 72     // 調配任務,設定任務多久後開始迴圈執行並間隔多久
 73     public void schedule(TimerTask task, long delay, long period) {
 74         if (delay < 0)
 75             throw new IllegalArgumentException("Negative delay.");
 76         if (period <= 0)
 77             throw new IllegalArgumentException("Non-positive period.");
 78         sched(task, System.currentTimeMillis()+delay, -period);
 79     }
 80 
 81     // 調配任務,設定任務迴圈執行時間並間隔多久
 82     public void schedule(TimerTask task, Date firstTime, long period) {
 83         if (period <= 0)
 84             throw new IllegalArgumentException("Non-positive period.");
 85         sched(task, firstTime.getTime(), -period);
 86     }
 87 
 88     // 調配任務,設定任務多久後開始迴圈執行並間隔多久
 89     public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
 90         if (delay < 0)
 91             throw new IllegalArgumentException("Negative delay.");
 92         if (period <= 0)
 93             throw new IllegalArgumentException("Non-positive period.");
 94         sched(task, System.currentTimeMillis()+delay, period);
 95     }
 96 
 97     // 調配任務,設定任務迴圈執行時間並間隔多久
 98     public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {
 99         if (period <= 0)
100             throw new IllegalArgumentException("Non-positive period.");
101         sched(task, firstTime.getTime(), period);
102     }
103 
104     // 任務調配
105     private void sched(TimerTask task, long time, long period) {
106         if (time < 0)
107             throw new IllegalArgumentException("Illegal execution time.");
108 
109         if (Math.abs(period) > (Long.MAX_VALUE >> 1))
110             period >>= 1;
111 
112         synchronized(queue) {
113             if (!thread.newTasksMayBeScheduled)
114                 throw new IllegalStateException("Timer already cancelled.");
115 
116             synchronized(task.lock) {
117                 if (task.state != TimerTask.VIRGIN)
118                     throw new IllegalStateException("Task already scheduled or cancelled");
119                 task.nextExecutionTime = time;
120                 task.period = period;
121                 task.state = TimerTask.SCHEDULED;
122             }
123 
124             queue.add(task);
125             if (queue.getMin() == task)
126                 queue.notify();
127         }
128     }
129 
130     // 中止任務, 一旦執行了這個方法timer就會結束掉
131     public void cancel() {
132         synchronized(queue) {
133             thread.newTasksMayBeScheduled = false;
134             queue.clear();
135             queue.notify();
136         }
137     }
138 
139     // 清除 Timer 中標記為 CANCELLED 的 TimerTask, 返回清除個數
140     public int purge() {
141         int result = 0;
142 
143         synchronized(queue) {
144             for (int i = queue.size(); i > 0; i--) {
145                 if (queue.get(i).state == TimerTask.CANCELLED) {
146                     queue.quickRemove(i);
147                     result++;
148                 }
149             }
150 
151             if (result != 0)
152                 queue.heapify();
153         }
154 
155         return result;
156     }
157 }
158 
159 
160 // 自定義執行緒類
161 class TimerThread extends Thread {
162 
163     boolean newTasksMayBeScheduled = true;
164 
165     private TaskQueue queue;
166 
167     TimerThread(TaskQueue queue) {
168         this.queue = queue;
169     }
170 
171     public void run() {
172         try {
173             mainLoop();
174         } finally {
175             synchronized(queue) {
176                 newTasksMayBeScheduled = false;
177                 queue.clear();
178             }
179         }
180     }
181 
182     // 執行任務
183     private void mainLoop() {
184         while (true) {
185             try {
186                 TimerTask task;
187                 boolean taskFired;
188                 synchronized(queue) {
189                     while (queue.isEmpty() && newTasksMayBeScheduled)
190                         queue.wait();
191                     if (queue.isEmpty())
192                         break;
193 
194                     long currentTime, executionTime;
195                     task = queue.getMin();
196                     synchronized(task.lock) {
197                         if (task.state == TimerTask.CANCELLED) {
198                             queue.removeMin();
199                             continue;
200                         }
201                         currentTime = System.currentTimeMillis();
202                         executionTime = task.nextExecutionTime;
203                         if (taskFired = (executionTime<=currentTime)) {
204                             if (task.period == 0) {        // 區分是否重複執行任務,間隔時間為 0 則執行一次
205                                 queue.removeMin();
206                                 task.state = TimerTask.EXECUTED;
207                             } else {
208                                 // 區分 schedule 和 scheduleAtFixedRate
209                                 queue.rescheduleMin(
210                                 task.period<0 ? currentTime   - task.period
211                                         : executionTime + task.period);
212                             }
213                         }
214                     }
215                     if (!taskFired)         // 下次執行時間大於當前時間 等待
216                         queue.wait(executionTime - currentTime);
217                 }
218                 if (taskFired)
219                     task.run();
220             } catch(InterruptedException e) {
221             }
222         }
223     }
224 }
225 
226 // 自定義任務管理類
227 class TaskQueue {
228 
229     // 初始化  128個空間,實際使用127個  位置編號為0的位置不使用
230     private TimerTask[] queue = new TimerTask[128];
231 
232     private int size = 0;
233 
234     // 任務佇列長度
235     int size() {
236         return size;
237     }
238 
239     // 新增任務,如果空間不足,空間*2,,然後排序(將nextExecutionTime最小的排到1位置)
240     void add(TimerTask task) {
241         if (size + 1 == queue.length)
242             queue = Arrays.copyOf(queue, 2*queue.length);
243 
244         queue[++size] = task;
245         fixUp(size);
246     }
247 
248     // 得到最小的nextExecutionTime的任務
249     TimerTask getMin() {
250         return queue[1];
251     }
252 
253     // 得到指定位置的任務
254     TimerTask get(int i) {
255         return queue[i];
256     }
257 
258     // 刪除最小nextExecutionTime的任務,排序(將nextExecutionTime最小的排到1位置)
259     void removeMin() {
260         queue[1] = queue[size];
261         queue[size--] = null;
262         fixDown(1);
263     }
264 
265     // 快速刪除指定位置的任務
266     void quickRemove(int i) {
267         assert i <= size;
268 
269         queue[i] = queue[size];
270         queue[size--] = null;
271     }
272 
273     // 重新設定最小nextExecutionTime的任務的nextExecutionTime,排序(將nextExecutionTime最小的排到1位置)
274     void rescheduleMin(long newTime) {
275         queue[1].nextExecutionTime = newTime;
276         fixDown(1);
277     }
278 
279     // 陣列是否為空
280     boolean isEmpty() {
281         return size==0;
282     }
283 
284     // 清空陣列
285     void clear() {
286         for (int i=1; i<=size; i++)
287             queue[i] = null;
288 
289         size = 0;
290     }
291 
292     // 將nextExecutionTime最小的排到1位置
293     private void fixUp(int k) {
294         while (k > 1) {
295             int j = k >> 1;
296             if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
297                 break;
298             TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
299             k = j;
300         }
301     }
302 
303     // 將nextExecutionTime最小的排到1位置
304     private void fixDown(int k) {
305         int j;
306         while ((j = k << 1) <= size && j > 0) {
307             if (j < size && queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
308                 j++; // j indexes smallest kid
309             if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
310                 break;
311             TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
312             k = j;
313         }
314     }
315 
316     // 排序(將nextExecutionTime最小的排到1位置)   在快速刪除任務後呼叫
317     void heapify() {
318         for (int i = size/2; i >= 1; i--)
319             fixDown(i);
320     }
321 }

5、TimerTask 原始碼

 1 package java.util;
 2 
 3 
 4 public abstract class TimerTask implements Runnable {
 5 
 6     final Object lock = new Object();
 7 
 8     int state = VIRGIN;         // 狀態 ,未使用,正在使用,非迴圈,使用完畢
 9 
10     static final int VIRGIN = 0;            // 未使用
11 
12     static final int SCHEDULED   = 1;       // 正在迴圈
13 
14     static final int EXECUTED    = 2;       // 非迴圈
15 
16     static final int CANCELLED   = 3;       // 使用完畢
17 
18     long nextExecutionTime;                 // 任務執行時間
19 
20     long period = 0;                        // 任務迴圈執行間隔時間
21 
22     protected TimerTask() {
23     }
24 
25     // 自定義任務
26     public abstract void run();
27 
28     // 退出   任務執行完畢後,退出返回 true ,未執行完 就退出 返回false
29     public boolean cancel() {
30         synchronized(lock) {
31             boolean result = (state == SCHEDULED);
32             state = CANCELLED;
33             return result;
34         }
35     }
36 
37     // 返回 時間
38     public long scheduledExecutionTime() {
39         synchronized(lock) {
40             return (period < 0 ? nextExecutionTime + period
41                     : nextExecutionTime - period);
42         }
43     }
44 }

6、Timer 與 TimerTask

Timer:Timer 是一個執行緒設施,可以用來實現某一個時間或某一段時間後安排某一個任務執行一次或定期重複執行。該功能需要和 TimerTask 類配合使用。每個 Timer 物件對應的是一個執行緒,因此計時器所執行的任務應該迅速完成,否則會延遲後續的任務執行。

TimerTask:TimerTask 用於實現Timer 類安排的一次或重複執行某個任務,而具體的任務內容在 TimerTask 的 run 方法中去實現。

7、Timer 定時器的使用方法

 1 package cn.pda.serialport;
 2 
 3 import java.util.Calendar;
 4 import java.util.Date;
 5 import java.util.Timer;
 6 import java.util.TimerTask;
 7 
 8 public class TimerTest {
 9 
10     public static void main(String[] args) {
11         // 宣告下,這裡單位為毫秒,所以 1000 毫秒為 1 秒
12         timer1();
13 //        timer2();
14 //        timer3();
15 //        timer4();
16     }
17 
18     // 第一種方法:實現 TimerTask 任務,指定在兩秒後執行
19     public static void timer1() {
20         Timer timer = new Timer();
21         timer.schedule(new TimerTask() {
22             @Override
23             public void run() {
24                 System.out.println("***** 自定義任務 *****");
25             }
26         }, 2000);
27     }
28 
29     // 第二種方法:實現 TimerTask 任務,指定在兩秒後迴圈執行,每次執行完後間隔五秒
30     public static void timer2() {
31         Timer timer = new Timer();
32         timer.schedule(new TimerTask() {
33             @Override
34             public void run() {
35                 System.out.println("***** 自定義任務 *****");
36             }
37         }, 2000, 5000);
38     }
39 
40     // 第三種方法:實現 TimerTask 任務,指定在兩秒後迴圈執行,每次執行完後間隔五秒
41     public static void timer3() {
42         Timer timer = new Timer();
43         timer.scheduleAtFixedRate(new TimerTask() {
44             @Override
45             public void run() {
46                 System.out.println("***** 自定義任務 *****");
47             }
48         }, 2000, 5000);
49     }
50 
51     // 第四種方法:實現 TimerTask 任務,指定在某個時間點迴圈執行,每次執行完間隔二十四小時
52     public static void timer4() {
53         Calendar calendar = Calendar.getInstance();
54         calendar.set(Calendar.HOUR_OF_DAY, 12);     // 控制時
55         calendar.set(Calendar.MINUTE, 0);           // 控制分
56         calendar.set(Calendar.SECOND, 0);           // 控制秒
57         Date time = calendar.getTime();             // 得出執行任務的時間,此處為今天的12:00:00
58         Timer timer = new Timer();
59         timer.scheduleAtFixedRate(new TimerTask() {
60             public void run() {
61                 System.out.println("***** 自定義任務 *****");
62             }
63         }, time, 1000 * 60 * 60 * 24);// 這裡設定將延時每天固定執行
64     }
65 
66 }