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的方法有三种:
- 执行shutdown(),但是这个会终止整个Executor
- task抛出异常,在task执行到需要中断的时候,抛出异常
- 取消当前执行的task
执行shutdown()肯定是不合适的,通过主动抛异常的方式来中断也不好
所以使用取消当前task的方式来实现自定义中断
- 首先看一下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实现自定义中断
- 初始化ScheduledExecutorService 线程池
private ScheduledExecutorService service = Executors.newScheduledThreadPool(20,new NamedThreadFactory("Schedule-Task"));
- 实现业务逻辑和退出条件
实现思路是通过保存每个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);
}