ScheduledExecutorService 简介
ScheduledExecutorService 的主要功能是将定时任务与线程池结合使用,网络上有很多博客对ScheduledExecutorService进行了分析,同时与Timer进行的比对。因此本篇不在对此进行描述,本篇主要描述使用ScheduledExecutorService的注意事项。
ScheduledExecutorService 代码原型
ScheduledExecutorService的构造函数源码是
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue());}
由于ScheduledThreadPoolExecutor的父类是ThreadPoolExecutor,因此构造函数可以是以下表现形式
ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue());
根据线程池参数的理解,分析如下
- 核心工作线程数 corePoolSize。
- 最大工作线程数 Integer.MAX_VALUE
- 非核心线程数无任务时存活时间 : 10S
- 阻塞队列,DelayedWorkQueue 是一个无界的阻塞队列。
- 由于阻塞队列是一个无界的队列,因此线程池工作的线程池数就是核心工作线程池数。
常用接口
延迟指定时间后,运行Runnable任务,Runnable的任务是没有返回值的,且不会向调用这抛出异常。
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
延迟指定时间后,运行Callable任务,Callable的任务有返回值,且通过get接口可以获取任务返回值和抛出的异常。
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
初次延迟initialDelay时间后,执行Runnable 任务,间隔period后,再次执行。当Runnable执行时间大于period,则间隔时间为Runnable的执行时间;当Runnable执行时间小于period,则间隔时间为period。在period和Runnable 时间具有可比性的时候,具有一定的影响。
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
初次延迟initialDelay时间后,执行Runnable 任务,之后间隔delay后,再次执行。注意间隔时间为任务执行时间+delay时间。任务之间的间隔时间是固定的。
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
注意事项
延迟时间和间隔时间的准确性问题
当核心工作线程数大于提交给线程池任务的数量时,代码中设置的延迟时间和隔间时间才是准确的。因此为了时间的准确性需要预先保证当核心工作线程数大于提交给线程池任务的数量。当提交的任务数无法确定时,最好自定义线程池,来满足时间的准确性问题。
任务必须执行成功
当任务必须执行成功时,必须考虑使用接口public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
通过返回值判断任务是否执行成功,如果执行失败,则可以把任务做一定的重试策略,重新添加到线程中执行。代码如下
private void test3(){Log.d(TAG, "===提交任务1==="+ DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));ret = scheduledThreadPool.schedule(callable,3000,TimeUnit.MILLISECONDS);new Thread(new Runnable() {@Overridepublic void run() {try {String strRet = ret.get();Log.d(TAG,"ret = "+strRet);if ("执行失败".equals(strRet)){// 再次执行,ret = scheduledThreadPool.schedule(callable,3000,TimeUnit.MILLISECONDS);}} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
周期性任务无法获取任务执行返回值问题
从java提供的接口来看,执行周期性任务无法获取执行结果,这样的原因是,scheduleAtFixedRate
和scheduleWithFixedDelay
。调用的源码是
// 核心是这个,sft是无返回指的,如果需要返回值,需要自定义线程池。
ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(period),sequencer.getAndIncrement());RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);