问:说说 Java 中 ScheduledExecutorService 与 Timer 的区别?
答:Timer 计时器具备使任务延迟执行以及周期性执行的功能,但是 Timer 天生存在一些缺陷,所以从 JDK 1.5 开始就推荐使用 ScheduledThreadPoolExecutor(ScheduledExecutorService 实现类)作为其替代工具。
首先 Timer 对提交的任务调度是基于绝对时间而不是相对时间的,所以通过其提交的任务对系统时钟的改变是敏感的(譬如提交延迟任务后修改了系统时间会影响其执行);而 ScheduledThreadExecutor 只支持相对时间,对系统时间不敏感。
接着 Timer 的另一个问题是如果 TimerTask 抛出未检查异常则 Timer 将会产生无法预料的行为,因为 Timer 线程并不捕获异常,所以 TimerTask 抛出的未检查异常会使 Timer 线程终止,所以后续提交的任务得不到执行;而 ScheduledThreadPoolExecutor 不存在此问题。
下面是 ScheduledExecutorService 接口提供的方法如下:
public interface ScheduledExecutorService extends ExecutorService {
/**
* 调度一个 Runnable 类型的 task,经过 delay(时间单位由参数 unit 决定)后开始进行调度。
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
* 调度一个 Callable 类型的 task,经过 delay(时间单位由参数 unit 决定)后开始进行调度。
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
* 周期性调度一个 Runnable 类型的 task,在 delay 后开始调度。
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**
* ScheduledExecutorService 相对于 Timer 所特有的方法,固定延迟调度。
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
下面是 ScheduledExecutorService 及其实现类的源码继承关系:
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {
......
}
public class ThreadPoolExecutor extends AbstractExecutorService {}
public abstract class AbstractExecutorService implements ExecutorService {}
public interface ExecutorService extends Executor {}
public interface ScheduledExecutorService extends ExecutorService {}
public interface ExecutorService extends Executor {}
可以看见 ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,所以本质上来说 ScheduledThreadPoolExecutor 是一个线程池,它也有 coorPoolSize 和 workQueue,也接受 Runnable 的子类作为任务,与一般线程池不一样的地方在于它实现了自己的工作队列 DelayedWorkQueue,这个队列会按照一定顺序对队列中的任务进行排序。
而对于 Timer,其实现为 Thread 加 Runnable 组成的列表。