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 ScheduledFutureextends Delayed, Future
这样就可以通过Future.cancel()的方式来取消当前task实现自定义中断
初始化ScheduledExecutorService 线程池
private ScheduledExecutorService service = Executors.newScheduledThreadPool(20,new NamedThreadFactory("Schedule-Task"));
实现业务逻辑和退出条件
实现思路是通过保存每个task的Future对象,在满足条件是,执行future.cancel()
// 当前正在执行的task合集
private Mapfutures = 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);
}