重拾 Java 高并发:聊聊 ScheduledThreadPoolExecutor 与 Timer 的区别

150 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情

前言

本篇文章,我们一起来聊聊 ScheduledThreadPoolExecutor、Timer 类的简单使用和实现细节及区别

  • JDK 1.5开始提供ScheduledThreadPoolExecutor类,重用线程池实现了任务的周期性调度功能。
  • JDK 1.5之前,实现任务的周期性调度主要使用的是 Timer 类和 TimerTask 类。

区别

先来看看两者的区别

线程角度

  • Timer 是单线程模式,如果某个 TimerTask 任务的执行时间比较久,会影响到其他任务的调度执行
  • ScheduledThreadPoolExecutor 是多线程模式,并且重用线程池,某个 ScheduledFutureTask 任务执行的时间比较久,不会影响到其他任务的调度执行。

是否捕获异常

  • Timer 不会捕获 TimerTask 抛出的异常。又因为加上 Timer 是单线程的,若某个调度任务出现异常,则整个线程就会终止,其他需要调度的任务也不再执行。
  • ScheduledThreadPoolExecutor 基于线程池来实现调度功能,某个任务抛出异常后,其他任务仍能正常执行。

任务优先级

  • Timer 中执行的 TimerTask 任务无优先级的概念,只是按照系统的绝对时间来执行任务。
  • ScheduledThreadPoolExecutor 中执行的 ScheduledFutureTask 类实现了 Comparable 接口(实现了compareTo 方法)和 java.util.concurrent.Delayed 接口(实现了 getDelay 方法)。compareTo方法实现了任务的比较,距离下次执行的时间间隔短的任务的优先级比较高getDelay方法则能够返回距离下次任务执行的时间间隔。

是否支持对任务排序

  • Timer不支持对任务的排序。
  • ScheduledThreadPoolExecutor 类中定义了一个静态内部类 DelayedWorkQueue,它是一个有序队列,为需要调度的每个任务按照距离下次执行时间间隔的大小来排序。

获取任务的返回结果

  • Timer 中执行的 TimerTask 类只是实现了 java.lang.Runnable 接口,无法从 TimerTask 中获取返回的结果。
  • ScheduledThreadPoolExecutor 中执行的 ScheduledFutureTask 类继承了 FutureTask 类,能够通过 Future 来获取返回的结果。

简单示例

Timer 示例

package com.zhongger.threadLearn;

import java.util.Timer;
import java.util.TimerTask;

/**
 * @author zhongmingyi
 * @date 2023/2/8 10:29 下午
 */
public class TimerTest {
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行Time Task");
            }
        }, 1000, 1000);
        Thread.sleep(10000);
        timer.cancel();
    }
}

ScheduledThreadPoolExecutor 示例

package com.zhongger.threadLearn;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author zhongmingyi
 * @date 2023/2/8 10:34 下午
 */
public class ScheduledThreadPoolExecutorTest {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("ScheduledThreadPoolExecutor 执行任务");
            }
        }, 1, 1, TimeUnit.SECONDS);
        Thread.sleep(10000);

        System.out.println("正在关闭线程池..."); // 关闭线程池
        scheduledExecutorService.shutdown();
        boolean isClosed;
        // 等待线程池终止
        do {
            isClosed = scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS);
            System.out.println("正在等待线程池中的任务执行完成");
        } while (!isClosed);
        System.out.println("所有线程执行结束,线程池关闭");
    }
}