Java Concurrent ScheduledExecutorService:定期排程的 thread pool

先前介紹過了使用 Java Timer 來達成工作排程的效果,
也介紹了 Java 內建超簡單的 Thread Pool 機制,
今天則是要來結合這兩者,
使用 Thread Pool 裡的 Thread 來執行定期排程的工作!

在開始之前,建議的大家可以先看一下先前的相關文章:
圖片來源:http://www.backupforall.com/backup%20scheduler.php

下面的程式中首先我們在 constructor 裡取得 2條 Thread 的 Scheduled Thread Pool,
service = Executors.newScheduledThreadPool(2);
接著一次進行一項測試,分別為:
testScheduleWork、testScheduleOutdatedWork 及 testScheduleMultiWork。

完整程式碼如下:
  1. package werdna1222coldcodes.blogspot.com.demo.scheduleTask;
  2.  
  3. import java.text.ParseException;
  4. import java.util.Date;
  5. import java.util.concurrent.Executors;
  6. import java.util.concurrent.ScheduledExecutorService;
  7. import java.util.concurrent.TimeUnit;
  8.  
  9. public class ScheduledThreadPoolDemo {
  10.  
  11. public static void main(String[] args) throws ParseException {
  12.  
  13. ScheduledThreadPoolDemo demo = new ScheduledThreadPoolDemo();
  14. // demo.testScheduleWork();
  15. demo.testScheduleOutdatedWork();
  16. // demo.testScheduleMultiWork();
  17. }
  18.  
  19. private ScheduledExecutorService service = null;
  20. public ScheduledThreadPoolDemo() {
  21. service = Executors.newScheduledThreadPool(2);
  22. }
  23.  
  24. void testScheduleWork() {
  25. Date current = new Date();
  26. System.out.println("***************testScheduleWork************
  27. ***");
  28. // schedule by delay
  29. Date date = new Date();
  30. date.setTime(current.getTime()+TimeUnit.MILLISECONDS.convert(
  31. 10, TimeUnit.SECONDS));
  32. service.schedule(new Work(1, date), 10, TimeUnit.SECONDS);
  33. // schedule by date
  34. Date date2 = new Date();
  35. date2.setTime(current.getTime()+TimeUnit.MILLISECONDS.convert(
  36. 5, TimeUnit.SECONDS));
  37. service.schedule(new Work(2, date2), date2.getTime()-current.
  38. getTime(), TimeUnit.MILLISECONDS);
  39. }
  40. void testScheduleOutdatedWork() {
  41. System.out.println("***************testScheduleOutdatedWork****
  42. ***********");
  43. Date current = new Date();
  44. Date date = new Date();
  45. // schedule by delay
  46. date.setTime(current.getTime()+TimeUnit.MILLISECONDS.convert(-
  47. 10, TimeUnit.SECONDS));
  48. service.schedule(new Work(1, date), date.getTime()-current.
  49. getTime(), TimeUnit.MILLISECONDS);
  50. }
  51. void testScheduleMultiWork() {
  52. System.out.println("***************testScheduleMultiWork*******
  53. ********");
  54. for (int i = 0; i < 5; i++) {
  55. Date date = new Date();
  56. long offset = (long)(Math.random()*10) - 5;
  57. date.setTime(date.getTime()+TimeUnit.MILLISECONDS.convert(
  58. offset, TimeUnit.SECONDS));
  59. System.out.println(date);
  60. service.schedule(new Work(i, date), date.getTime()-new
  61. Date().getTime(), TimeUnit.MILLISECONDS);
  62. }
  63. }
  64. class Work implements Runnable {
  65. private int id;
  66. private Date date;
  67. public Work (int id, Date date) {
  68. this.id = id;
  69. this.date = date;
  70. }
  71. public void run() {
  72. System.out.println(Thread.currentThread().getName() + "
  73. Begins Work " + id);
  74. System.out.println("Work " + id + " Scheduled Time: " +
  75. date.toString());
  76. System.out.println("Work " + id + " Start Time: " + new
  77. Date().toString());
  78. try {
  79. Thread.sleep(5000);
  80. }
  81. catch (InterruptedException ex) {
  82. ex.printStackTrace();
  83. }
  84. System.out.println("Work " + id + " End Time: " + new Date(
  85. ).toString());
  86. System.out.println(Thread.currentThread().getName() + "
  87. Ends Work " + id);
  88. }
  89. }
  90. }

在 testScheduleOutdatedWork 裡我們測試了如果 delay 是負的或 date 已過期的狀況,
結果會馬上執行補做喔,而不是就不做了!
像下面的結果程式開始的時間是 21:17:09,排程的時間是20秒前的 21:16:59,
所以程式一執行他馬上就補做了!
***************testScheduleOutdatedWork***************
pool-1-thread-1 Begins Work 1
Work 1 Scheduled Time: Wed Nov 07 21:16:59 CST 2012
Work 1 Start Time: Wed Nov 07 21:17:09 CST 2012
Work 1 End Time: Wed Nov 07 21:17:14 CST 2012
pool-1-thread-1 Ends Work 1

最後在 testScheduleMultiWork 裡我們是要測試 Thread Pool 的效果,
仔細觀察可以發現奇妙的結果喔!
程式在 21:28:43 開始後馬上補做 28:38, 28:39 兩個過期的工作,
而在工作執行期間 44, 45兩個工作也應該要馬上做了,
但因為 Pool 中只有兩條 Thread,所以這兩項工作被 delay,要等前面的工作做完才做。
同理我們看到最後執行的 Work 3,排程的時間是 47,但開始執行時已經是 53囉!
***************testScheduleMultiWork***************
Wed Nov 07 21:28:44 CST 2012
Wed Nov 07 21:28:45 CST 2012
Wed Nov 07 21:28:39 CST 2012
Wed Nov 07 21:28:47 CST 2012
Wed Nov 07 21:28:38 CST 2012
pool-1-thread-2 Begins Work 2
pool-1-thread-1 Begins Work 4
Work 2 Scheduled Time: Wed Nov 07 21:28:39 CST 2012
Work 4 Scheduled Time: Wed Nov 07 21:28:38 CST 2012
Work 2 Start Time: Wed Nov 07 21:28:43 CST 2012
Work 4 Start Time: Wed Nov 07 21:28:43 CST 2012
Work 2 End Time: Wed Nov 07 21:28:48 CST 2012
Work 4 End Time: Wed Nov 07 21:28:48 CST 2012
pool-1-thread-2 Ends Work 2
pool-1-thread-1 Ends Work 4
pool-1-thread-1 Begins Work 0
pool-1-thread-2 Begins Work 1
Work 0 Scheduled Time: Wed Nov 07 21:28:44 CST 2012
Work 1 Scheduled Time: Wed Nov 07 21:28:45 CST 2012
Work 0 Start Time: Wed Nov 07 21:28:48 CST 2012
Work 1 Start Time: Wed Nov 07 21:28:48 CST 2012
Work 0 End Time: Wed Nov 07 21:28:53 CST 2012
Work 1 End Time: Wed Nov 07 21:28:53 CST 2012
pool-1-thread-1 Ends Work 0
pool-1-thread-2 Ends Work 1
pool-1-thread-1 Begins Work 3
Work 3 Scheduled Time: Wed Nov 07 21:28:47 CST 2012
Work 3 Start Time: Wed Nov 07 21:28:53 CST 2012
Work 3 End Time: Wed Nov 07 21:28:58 CST 2012
pool-1-thread-1 Ends Work 3

以上的程式供大家參考,其實還有其他好用的 schedule 方法,下面簡單介紹給大家。
service = Executors.newScheduledThreadPool(2);

// 只要有空的 Thread 就直接執行
service.execute(command);

// 固定週期,如:initialDelay, initialDelay+period, initialDelay+2*period ……
// 下一個工作開始時間以前一工作開始時間加上 period 計算
service.scheduleAtFixedRate(command, initialDelay, period, unit);

// 固定延遲, 下一個工作開始時間以前一工作結束時間加上 delay 計算
service.scheduleWithFixedDelay(command, initialDelay, delay, unit);

若想知道更多有關 Java 時間相關的轉換、排程等應用,
或是想看其他我所寫關於 Java 的範例程式,請見:Java 教學及程式範例大全

本文出自符碼記憶,請勿全文轉載,部份轉載請註明出處關鍵字:Java, Thread, Pool, Executor, Executors, ThreadExecutor, ThreadPoolExecutor, ScheduledThreadPool, execute, scheduleAtFixedRate, scheduleWithFixedDelay, schedule
參考資料:

這個網誌中的熱門文章

【銀行代碼查詢】3碼銀行代碼列表、7碼分行代碼查詢

【台北中壢】國道客運/公車路線(1818,2022,9001,9025)!

【博客來折價券】博客來免費序號e-coupon分享(持續更新)

【Hami Video】影劇/電視/運動館免費體驗/試用序號分享(隨時更新)!

【博客來折價券】25/50/100/212/400/500現領現折+天天簽到換200+OP兩倍換!