ScheduledThreadPoolExecutor
是ThreadPoolExecutor
的子类,同时实现了ScheduledExecutorService
接口,它可另行安排在给定的延迟后运行任务(Callable,Runnable),或者定期执行命令。此类要优于 Timer
。
虽然此类继承自 ThreadPoolExecutor,但是几个继承的调整方法对此类并无作用。特别是,因为它作为一个使用 corePoolSize
线程和一个无界队列的固定大小的池,所以调整 maximumPoolSize
没有什么效果。
可以看到ScheduledThreadPoolExecutor实现了ScheduledExecutorService接口,延迟或者定时执行某个任务的方法,都定义在这个接口中
public interface ScheduledExecutorService extends ExecutorService { //延迟指定时间来执行某个任务 public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit); public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit); //延迟指定时间后,定时执行某个任务 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); // public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); }
注意,这几个方法返回的都是ScheduledFuture
接口,其是Future的子接口,其只有一个实现:ScheduledFutureTask
,ScheduledFutureTask同时也扩展了FutureTask,(这与我们之前讲解支持异步监听类似,当我们希望Future具备更加强大的能力时,则需要进行相应的扩展)
ScheduledThreadPoolExecutor重写了 AbstractExecutorService
的 submit
方法,以生成内部ScheduledFutureTask 对象控制每个任务的延迟和调度。这个和我们在上一节的案例中,扩展ThreadPoolExecutor以支持异步监听类似,只不过上一节我们是覆盖newTaskFor
方法,回顾newTaskFor就是在submit方法中调用的,相关代码片段如下:
public abstract class AbstractExecutorService implements ExecutorService { ... public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask);//构造之后,立即执行 return ftask; } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } ... }
在ScheduledThreadPoolExecutor中之所以选择直接覆盖submit方法而不是newTaskFor方法,因为AbstractExecutorService中调用完成newTaskFor方法之后就立即执行了(调用execute),我们这里要实现的是延迟,或者定时执行某个任务,不能立即执行,所以选择覆盖submit方法,进行完全重写。
接下来,我们看看ScheduledThreadPoolExecutor如何使用,之后再分析器源码
Executors
E类提供了4个工厂方法,可以帮助我们快速的创建ScheduledThreadPoolExecutor
指定延迟时间执行:
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit) public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) |
这两个方法的作用相同,只是参数类型不同,都是给定延迟时间执行某个任务,任务只会被执行一次
//任务提交时间,延迟10秒执行 public static void testSchedule(){ System.out.println("Submit Time = "+new Date().toLocaleString()); scheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println("ScheduledTask.run-------"+new Date().toLocaleString()); } }, 10, TimeUnit.SECONDS); }
考虑任务执行时间
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
//延迟10秒执行,之后每2秒执行一次 public static void scheduleAtFixedRate(){ System.out.println("Submit Time = "+new Date().toLocaleString()); scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ScheduledTask.run-------"+new Date().toLocaleString()); } }, 10, 2,TimeUnit.SECONDS); }
自动校正任务的执行时间,也就是说,如果任务执行了1秒,那么再过1秒就会执行,如果任务执行了1. 5秒,那么再过0. 5秒就会执行
Submit Time = 2017-1-15 22:48:17 ScheduledTask.run-------2017-1-15 22:48:28 ScheduledTask.run-------2017-1-15 22:48:30 ScheduledTask.run-------2017-1-15 22:48:32 |
scheduleWithFixedDelay:不考虑任务执行时间 ,总是在上一次任务执行完成之后,再延迟指定时间进行执行
//延迟10秒执行,之后每2秒执行一次 public static void scheduleWithFixedDelay(){ System.out.println("Submit Time = "+new Date().toLocaleString()); scheduledThreadPool.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ScheduledTask.run-------"+new Date().toLocaleString()); } }, 10, 2,TimeUnit.SECONDS); }
输出
Submit Time = 2017-1-15 22:50:44 ScheduledTask.run-------2017-1-15 22:50:55 ScheduledTask.run-------2017-1-15 22:50:58 ScheduledTask.run-------2017-1-15 22:51:01 ScheduledTask.run-------2017-1-15 22:51:04 |