2011年11月29日 星期二

Java 定時執行 (1)

Java 中要定時執行某個動作,有三種選擇:
1、Timer 和 TimerTask
2、ScheduledExecutorService
3、quartz 套件
原本我是用 1 的方法來實作定時執行,但後來遇到 Timer 的 thread 難以控制的問題
如果定時執行到一半突然必須強制關閉時,Timer 很難做到讓我找出它的 thread 並且關掉
在查了一些資料後看到這篇文章:Java Timer vs ExecutorService?
其中回應裡有一段「ScheduledThreadPoolExecutor can be configured with any number of threads. Furthermore, you have full control over created threads, if you want (by providing ThreadFactory)」
而 quartz 因為不明原因把網路上找到的 sample code 貼上去以後程式就爆了
而且有看到有文章說 ScheduledExecutorService 是比較底層的 API,雖然很多事情要自己做
但也因此能夠自己對 Thread 有比較完整的控制權
因此最後是決定改用 ScheduledExecutorService 來實作定時執行。


指定時間只執行一次:

import java.util.Calendar;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class OneShot{
  private ScheduledExecutorService scheduler;
  
  public OneShot() {
    scheduler = Executors.newScheduledThreadPool(1);
  }
  
  public void addTask (long updateTime) {
    long delay = 0;
    long currentTime = Calendar.getInstance().getTimeInMillis();
    // 指定的時間還沒到就排程執行
    if(updateTime > currentTime)
      scheduler.schedule(new Shot(), delay, TimeUnit.MILLISECONDS);  }
  
  private class Shot implements Runnable {
    public Shot() {}
    
    public void run() {
      .................
    }
  }
}

排程定時執行:

import java.util.Calendar;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduleTask {
  private ScheduledExecutorService scheduler;
  
  public ScheduleTask() {
    scheduler = Executors.newSingleThreadScheduledExecutor();
  }
  
  public void addTask (long delay, long breakTime) {
    scheduler.scheduleWithFixedDelay(new Shot(), delay, breakTime, TimeUnit.MILLISECONDS);
  }
  
  private class Shot implements Runnable {
    public Shot() {}
    
    public void run() {
      .................
    }
  }
}

要執行時只要實例化之後呼叫 AddTask() 並且給予適當的參數即可。

// 執行一次
OneShot oneShot = new OneShot();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 10); // 設定 10 秒後執行
oneShot.addTask(cal.getTimeInMillis());

ScheduleTask schedule = new ScheduleTask();
schedule.addTask(1000, 60000); // 排程 1 秒後開始,每分鐘執行一次

目前還沒開始做 ThreadFactory,不過要控制 Thread 應該是需要 ThreadFactory 來解決。
2012.1.11 更新:請參考續篇 Java 定時執行 (2):將 ExecutorService 產生的 Thread 加入 ThreadGroup

參考資料:
在Timer和ScheduledExecutorService间决择
How to use ThreadFactory in Java
ThreadFactory usage in Java

沒有留言: