Java 定時調配 Timer 類和定任務 TimerTask 類
阿新 • • 發佈:2020-12-30
前言
在我們日常生活中,我們常常會遇到有關計時器的事情。如商城類專案會在某年某月某日某時某分某秒進行特價活動,那麼當時間到達這個時間點上的時候該事件就會觸發。
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 }