Java可自定義中斷定時器的實現

weixin_34279579發表於2016-06-22

Java定時器的實現一般使用的是Timer和ScheduledExecutorService

  • 使用Timer的時候需要自定義一個task繼承TimerTask,在task中封裝業務邏輯
    然後再使用Timer.scheduleAtFixedRate 按照固定速率執行task中的邏輯

本文介紹的是ScheduledExecutorService來實現可中斷定時器
我們首先看一下scheduleAtFixedRate 的javaDoc

  • If any execution of the task encounters an exception, subsequent executions are suppressed.Otherwise, the task will only terminate via cancellation or termination of the executor.

通過javaDoc可以發現需要終止task的方法有三種:

  1. 執行shutdown(),但是這個會終止整個Executor
  2. task丟擲異常,在task執行到需要中斷的時候,丟擲異常
  3. 取消當前執行的task

執行shutdown()肯定是不合適的,通過主動拋異常的方式來中斷也不好
所以使用取消當前task的方式來實現自定義中斷

  1. 首先看一下scheduleAtFixedRate的原始碼,我們注意到這個方法返回的是ScheduledFuture<?>
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

ScheduledFuture是繼承了Future的

public interface ScheduledFuture<V> extends Delayed, Future<V>

這樣就可以通過Future.cancel()的方式來取消當前task實現自定義中斷

  1. 初始化ScheduledExecutorService 執行緒池
private ScheduledExecutorService service = Executors.newScheduledThreadPool(20,new NamedThreadFactory("Schedule-Task"));
  1. 實現業務邏輯和退出條件
    實現思路是通過儲存每個task的Future物件,在滿足條件是,執行future.cancel()
// 當前正在執行的task合集 
private Map<String,Future> futures = new HashMap<>();
// 執行定時任務的時間間隔
private int period = 5;
private String taskPrefix ="DoSomething-Task";
public void executeTask(String id) {

    // 已有當前定時任務在執行時,刪掉當前任務,然後重新啟動
    if (futures.containsKey(taskPrefix+id)) {
        futures.get(taskPrefix+id).cancel(true);
        futures.remove(taskPrefix+id);
    }
    Future future = service.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            int currentNum = cacheService.getCurrentShowNum(id);
            // 滿足中斷task條件
            if ( /* 條件邏輯 */) {
                Future currentFuture = futures.get(taskPrefix+id);
                currentFuture.cancel(true);
                // 任務執行完成
                futures.remove(taskPrefix+id);
            } else {
                // 執行task業務邏輯
            }
        }
    },0,period, TimeUnit.MINUTES);
    futures.put(taskPrefix+id,future);
}

相關文章