JAVA ScheduledExecutorService(ScheduledThreadPoolExecutor)实现定时任务(替代Timer )

提出问题

JAVA Timer 和 TimerTask 的缺点

解决

使用 ScheduledExecutorService (接口)、ScheduledThreadPoolExecutor(前面接口的实现类)替代 Timer

线程数可以自行控制,基于多线程机制,任务之间不会相互影响

介绍

ScheduledExecutorService :是接口

ScheduledThreadPoolExecutor :是上面接口的实现类,该类继承 ThreadPoolExecutor 类

创建对象

创建 ScheduledThreadPoolExecutor 对象,有两种方法:

  • 通过 ScheduledThreadPoolExecutor 构造方法

  • Executors 工厂方法创建

根据阿里巴巴代码规范中的建议,推荐使用第一种方式

通过 构造方法 创建对象

参数最全的构造方法:

ScheduledThreadPoolExecutor(
    int corePoolSize,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler
)

参数解释:

  • corePoolSize:核心线程数量

  • threadFactory:线程工厂,用来创建线程,默认为 Executors.defaultThreadFactory()

  • handler: 拒绝策略,默认为 AbortPolicy 中止策略

底层代码

上面构造方法,底层在调用 ThreadPoolExecutor 类的构造方法,如下:

super(
    corePoolSize,        // 形参传入
    Integer.MAX_VALUE,  // 最大线程数
    0,                    // 空闲线程存活时间
    NANOSECONDS,        // 空闲线程存活时间 的单位,纳秒
    new DelayedWorkQueue(), // 任务队列,使用内部类
    threadFactory, // 线程工厂
    handler // 拒绝策略
);

通过 Executors 创建对象

注意:不要使用该方式,详见 链接

通过 newSingleThreadScheduledExecutor() 方法

通过 newSingleThreadScheduledExecutor() 方法创建对象,这个对象的线程池大小为 1 ,如果任务多于一个,任务将按先后顺序执行,即:串行

方式一

Executors.newSingleThreadScheduledExecutor()

方式二

Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

参数解释:

  • ThreadFactory:线程工厂,用于自定义线程的名字,daemon,优先级等

    • 可以使用 Executors.DefaultThreadFactory 类对象
    • 也可以自定义,实现 ThreadFactory 接口

通过 newScheduledThreadPool() 方法

创建对象,可以指定线程池大小

方法一

Executors.newScheduledThreadPool(int corePoolSize);

参数解释:

  • corePoolSize:线程数量

方法二

Executors.newScheduledThreadPool(
    int corePoolSize,
    ThreadFactory threadFactory
);

参数解释:

  • corePoolSize:线程数量

  • ThreadFactory:线程工厂,用于自定义线程的名字,daemon,优先级等

    • 可以使用 Executors.DefaultThreadFactory 类对象
    • 也可以自定义,实现 ThreadFactory 接口

常用功能方法

定时执行任务的方法:

  • schedule()
  • scheduleAtFixedRate()
  • scheduleWithFixedDelay()

停止任务的方法,详见 shutdown() 、 shutdownNow()

schedule方法

延时执行任务,仅执行一次

schedule(
    Runnable command, // Runnable实现类
    long delay, // 延迟时间
    TimeUnit unit // 延迟时间单位
)

例子

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test_schedule {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println(sdf.format(new Date()));
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.schedule(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("run..."+sdf.format(new Date()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },3, TimeUnit.SECONDS);//延迟3秒后执行 run() 方法,仅执行一次
        // 可以调用 shutdown() 方法
        executor.shutdown();
    }
}

执行结果:

17:25:59
run...17:26:02 // 延迟3秒执行,且执行一次

schedule方法2

延时执行任务,仅执行一次,执行后可通过 Future.get() 阻塞直至任务执行完毕,并且可以获取执行结果

schedule(
    Callable<V> callable, // Callable实现类
    long delay, // 延迟时间
    TimeUnit unit // 延迟时间单位
)

scheduleAtFixedRate 方法

循环任务,按照 上一次任务的开始时间 计算 下一次任务的开始时间

例子

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test_scheduleAtFixedRate {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println(sdf.format(new Date()));
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("开始执行..."+sdf.format(new Date()));
                    Thread.sleep(2000);
                    System.out.println("结束执行..."+sdf.format(new Date()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },3,5, TimeUnit.SECONDS);//延迟3秒后执行第一次 run() 方法,之后每隔 5 秒执行一次 run() 方法
        // 不能调用 shutdown() 方法,否则直接退出
        // executor.shutdown();
    }


}

执行结果:

19:03:14
开始执行...19:03:17 // 延迟3秒执行
结束执行...19:03:19 // 任务执行2秒
开始执行...19:03:22 // 本次任务开始执行时间 = 上次任务开始执行时间 + 5秒
结束执行...19:03:24
......

scheduleWithFixedDelay

循环任务,以 上一次任务的 结束时间 计算 下一次任务的开始时间

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test_scheduleWithFixedDelay {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println(sdf.format(new Date()));
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("开始执行..."+sdf.format(new Date()));
                    Thread.sleep(2000);
                    System.out.println("结束执行..."+sdf.format(new Date()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },3,5, TimeUnit.SECONDS);//延迟3秒后执行第一次 run() 方法,之后每隔 5 秒执行一次 run() 方法
        // 不能调用 shutdown() 方法,否则直接退出
        // executor.shutdown();
    }


}

执行结果:

19:07:31
开始执行...19:07:34 // 延迟3秒执行
结束执行...19:07:36 // 任务执行2秒
开始执行...19:07:41 // 本次任务开始执行时间 = 上次任务结束执行时间 + 5秒
结束执行...19:07:43
...

多线程对象

可以并行执行多个任务,创建对象方式不同,使用方法相同

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test_scheduleAtFixedRate {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println(sdf.format(new Date()));
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("开始执行任务1..."+sdf.format(new Date()));
                    Thread.sleep(2000);
                    System.out.println("结束执行任务1..."+sdf.format(new Date()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },3,5, TimeUnit.SECONDS);//延迟3秒后执行第一次 run() 方法,之后每隔 5 秒执行一次 run() 方法

        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("开始执行任务2..."+sdf.format(new Date()));
                    Thread.sleep(2000);
                    System.out.println("结束执行任务2..."+sdf.format(new Date()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },1,3, TimeUnit.SECONDS);//延迟3秒后执行第一次 run() 方法,之后每隔 5 秒执行一次 run() 方法


        // 不能调用 shutdown() 方法,否则直接退出
        // executor.shutdown();
    }


}

参考:
https://www.jianshu.com/p/aeb391e4edb0
https://blog.csdn.net/xixisi888/article/details/108602699
https://www.jianshu.com/p/aeb391e4edb0


原文出处:/show_1IX4mK4GOoUZ.html