Java定时器vs ExecutorService?

我有使用java.util.Timer调度任务的代码。我环顾四周,看到ExecutorService可以做同样的事情。所以这个问题在这里,你是否使用TimerExecutorService来调度任务,使用一个比另一个有什么好处?

还想检查是否有人使用了Timer类,并遇到了ExecutorService为他们解决的任何问题。

124172 次浏览

ExecutorService更新且更通用。计时器只是一个线程,它周期性地运行你为它安排的事情。

ExecutorService可以是一个线程池,甚至可以分布在集群中的其他系统中,执行诸如一次性批处理执行之类的事情……

只要看看每家公司提供的服务就能决定。

根据Java并发性实践:

  • Timer可以对系统时钟的变化敏感,而ScheduledThreadPoolExecutor则不是。
  • Timer只有一个执行线程,所以长时间运行的任务可能会延迟其他任务。ScheduledThreadPoolExecutor可以配置任意数量的线程。此外,如果你愿意,你可以完全控制创建的线程(通过提供ThreadFactory)。
  • TimerTask中抛出的运行时异常杀死了这个线程,从而使Timer死亡:-(…即计划任务将不再运行。ScheduledThreadExecutor不仅捕获运行时异常,而且允许你在需要时处理它们(通过从ThreadPoolExecutor重写afterExecute方法)。抛出异常的任务将被取消,但其他任务将继续运行。

如果你可以使用ScheduledThreadExecutor代替Timer,那么就这样做。

还有一件事……虽然ScheduledThreadExecutor在Java 1.4库中不可用,但有一个JSR 166 (java.util.concurrent)的后端口到Java 1.2, 1.3, 1.4,它有ScheduledThreadExecutor类。

如果它对你可用,那么很难想出一个理由使用Java 5执行器框架。调用:

ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();

将给你一个与Timer功能相似的ScheduledExecutorService(即它将是单线程的),但其访问可能更具可伸缩性(在底层,它使用并发结构,而不是像Timer类那样完全同步)。使用ScheduledExecutorService也会给你带来一些好处,比如:

  • 如果需要,你可以自定义它(参见newScheduledThreadPoolExecutor()ScheduledThreadPoolExecutor类)
  • “一次性”执行可以返回结果

我能想到的坚持Timer的唯一原因是:

  • 它在Java 5之前可用
  • J2ME中提供了一个类似的类,它可以使移植应用程序更容易(但在本例中添加一个公共抽象层并不难)

我有时更喜欢Timer而不是Executors.newSingleThreadScheduledExecutor()的原因是,当我需要在守护线程上执行计时器时,我得到了更清晰的代码。

比较

private final ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
};
private final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(threadFactory);

private final Timer timer = new Timer(true);

当我不需要executorservice的健壮性时,我就这样做。

从Oracle文档页ScheduledThreadPoolExecutor

一个ThreadPoolExecutor,它可以额外安排命令在给定的延迟后运行,或定期执行。当需要多个工作线程时,或者当需要ThreadPoolExecutor(该类扩展)的额外灵活性或功能时,该类比计时器更可取。

当你有多个工作线程时,ExecutorService/ThreadPoolExecutorScheduledThreadPoolExecutor是明显的选择。

ExecutorServiceTimer的优点

  1. ExecutorService不同,Timer不能利用可用的CPU内核,特别是在使用ForkJoinPoolExecutorService类型的多个任务时
  2. 如果你需要多个任务之间的协调,ExecutorService提供了协作API。假设您必须提交N个worker任务,并等待它们全部完成。你可以通过invokeAll API轻松实现。如果你想在多个Timer任务中达到同样的效果,这并不简单。
  3. ThreadPoolExecutor为线程生命周期管理提供了更好的API。

    线程池解决了两个不同的问题:由于减少了每个任务的调用开销,它们通常在执行大量异步任务时提供改进的性能;它们提供了一种约束和管理执行一组任务时消耗的资源(包括线程)的方法。每个ThreadPoolExecutor还维护一些基本统计信息,例如已完成任务的数量

    几个优势:

    a.创建/管理/控制线程的生命周期优化线程创建成本开销

    b.你可以控制任务的处理(Work Stealing, ForkJoinPool, invokeAll)等。

    c.可以监视线程的进度和运行状况

    d.提供更好的异常处理机制

我确实有定时器的问题,我替换它与ScheduledExecutorService来修复它。

问题是计时器依赖于系统时间,每次我改变它,它会影响应用程序的功能。所以我用ScheduledExecutorService替换了计时器,现在它工作正常。