Java并发39:Executor系列--ScheduleExecutorService接口学习笔记

2021/9/2 20:36:22

本文主要是介绍Java并发39:Executor系列--ScheduleExecutorService接口学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

本章主要学习ScheduleExecutorService接口。

1.ScheduleExecutorService接口概述

ScheduledExecutorService继承自ExecutorService,它可以在给定延时之后调度任务,也可以以指定的周期调度任务。

schedule()方法可以创建含有延时(delays)变量的任务,然后返回一个可以用于取消或检查运行状态的Future对象。

scheduleAtFixedRate()方法和scheduleWithFixedDelay()方法可以创建并运行定期运行的任务。


通过Executor#execute(Runnable)方法和ExecutorService.submit()方法提交的命令会作为零延时(delay=0)的任务被调度。

在调用schedule()方法时,如果传递0或者负值的延时参数,这些任务将被当做立刻执行的任务。


所有的schedule()方法都接收相对延时和周期作为参数,但是并不接受绝对时间或者绝对日期。

将java.util.Date表示的绝对日期转换成一个绝对日期是一件很简单的事情。

例如,计划在将来的某个时刻执行任务,你可以这么做:

schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);

但是需要注意,由于时间同步协议、时钟漂移或其他因素,相对延时的到期时间未必与预期的Date时间一致。


Executors工具类提供了方便的工厂方法用于实现ScheduledExecutorService。

示例

下面这个类的beepForAnHour方法启动了一个ScheduledExecutorService服务,这个服务每个小时内有10秒发出哔哔哔的声音。

复制代码
import static java.util.concurrent.TimeUnit.*;
class BeeperControl {
    private final ScheduledExecutorService scheduler =
            Executors.newScheduledThreadPool(1);

    public void beepForAnHour() {
        final Runnable beeper = new Runnable() {
            public void run() {
                System.out.println("beep");
            }
        };
        final ScheduledFuture<?> beeperHandle =
                scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
        scheduler.schedule(new Runnable() {
            public void run() {
                beeperHandle.cancel(true);
            }
        }, 60 * 60, SECONDS);
    }
}
复制代码

2.Executor、ExecutorService与ScheduleExecutorService

下面对Executor、ExecutorService和ScheduleExecutorService三个接口的特性进行简单的说明:

  • Executor
    • 执行Runnable接口:execute
  • ExecutorService
    • 执行Runnable接口:execute
    • 手动关闭:shutdown、shutdownNow
    • 执行Future和Callable接口:submit、invokeAll、invokeAny
  • ScheduleExecutorService
    • 执行Runnable接口:execute
    • 手动关闭:shutdown、shutdownNow
    • 执行Future和Callable接口:submit、invokeAll、invokeAny
    • 延时调度、周期调度:schedule、scheduleWithFixedDelay、scheduleAtFixedRate

3.ScheduleExecutorService方法说明

ScheduleExecutorService的延时调度、周期调度相关方法如下:

1.schedule(Runnable command,long delay, TimeUnit unit)

  • 在一定延时(delay)之后,运行Runnable任务。
  • 此任务只运行一次。

2.schedule(Callable callable,long delay, TimeUnit unit)

  • 在一定延时(delay)之后,运行Callable任务。
  • 此任务只运行一次。

3.scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)

  • 在一定延时(initialDelay)之后,开始周期性的运行Runnable任务。
  • 周期性:上一次任务执行完成之后,等待一段时间(delay),然后开始下一次任务。

4.scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)

  • 在一定延时(initialDelay)之后,开始周期性的运行Runnable任务。
  • 周期性:每过一段时间(period),就开始运行一次Runnable任务。
  • 如果任务的执行时间大于等待周期(period):
    • 上一次任务执行完成之后,立即开始下一次任务。
    • 也就是说:每过一段时间(任务执行时间),就开始运行一次Runnable任务。

4.实例练习

练习目的:掌握延时调度任务和周期调度任务的方法的用法。

练习内容:

  • 分别实现以下调度任务:
  • 方式1:通过schedule方法实现:2秒之后打印系统时间。
  • 方式2:通过scheduleWithFixedDelay方法实现:5秒之后开始周期性的打印系统时间,连续两次打印间隔为3秒(delay),每次打印耗时2秒。
  • 方式3:通过scheduleAtFixedRate方法实现:5秒之后开始周期性的打印系统时间,每3秒(period)打印一次,每次打印耗时2秒。
  • 方式3:通过scheduleAtFixedRate方法实现:5秒之后开始周期性的打印系统时间,每2秒(period)打印一次,每次打印耗时3秒。

实例代码:

复制代码
  1 /**
  2  * <p>ScheduleExecutorService</p>
  3  *
  4  **/
  5 public static void main(String[] args) throws InterruptedException, ExecutionException {
  6     /*
  7     Executor                    执行Runnable接口
  8     ExecutorService             执行Runnable接口、手动关闭、执行Future和Callable接口(单个、任一、批量)
  9     ScheduleExecutorService     执行Runnable接口、手动关闭、执行Future和Callable接口(单个、任一、批量)、
 10                                 延时执行Runnable接口和Callable接口、周期执行Runnable接口(2种方式)
 11      */
 12 
 13     ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
 14     /*
 15     0   schedule                延时执行Runnable和延时执行Callable   执行一次
 16     1   scheduleWithFixedDelay  周期性的延时执行Runnable接口      上一次任务结束和下一次任务开始之间的时间间隔是固定的=delay
 17     2   scheduleAtFixedRate     周期性的等速率执行Runnable接口     上一次任务开始和下一次任务开始之间的时间间隔是固定的=period
 18     3   scheduleAtFixedRate     周期性的等速率执行Runnable接口     如果任务执行时间大于period,则上一次任务结束之后,立即开始下一次任务;即period=任务执行时间
 19      */
 20     int type = 0;
 21     switch (type) {
 22         case 0:
 23             //延时执行Runnable接口
 24             LOGGER.info("延时执行Runnable接口 : " + System.currentTimeMillis());
 25             scheduledExecutorService.schedule(() -> {
 26                 LOGGER.info("2秒之后 : " + System.currentTimeMillis());
 27             }, 2000, TimeUnit.MILLISECONDS);
 28 
 29             Thread.sleep(2500);
 30             //延时执行Callable接口
 31             System.out.println();
 32             LOGGER.info("延时执行Callable接口 : " + System.currentTimeMillis());
 33             ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(() -> {
 34                 return System.currentTimeMillis();
 35             }, 2, TimeUnit.SECONDS);
 36             LOGGER.info("2秒之后 :" + scheduledFuture.get());
 37 
 38             //等待多长时间
 39             Thread.sleep(1000);
 40             break;
 41         case 1:
 42             //周期性的延时执行
 43             //初始延时
 44             long initDelay = 5000;
 45             //延时
 46             long delay = 3000;
 47             LOGGER.info("周期性的延时执行Runnable接口 : " + System.currentTimeMillis());
 48             //周期性的延时执行
 49             scheduledExecutorService.scheduleWithFixedDelay(() -> {
 50                 int number = RandomUtils.nextInt(1000, 3000);
 51                 LOGGER.info("周期性的延时执行Runnable接口 [" + number + "]开始运行 : " + System.currentTimeMillis());
 52                 //模拟运行
 53                 try {
 54                     Thread.sleep(2000);
 55                 } catch (InterruptedException e) {
 56                     e.printStackTrace();
 57                 }
 58                 LOGGER.info("周期性的延时执行Runnable接口 [" + number + "]结束运行 : " + System.currentTimeMillis());
 59             }, initDelay, delay, TimeUnit.MILLISECONDS);
 60 
 61             //等待多长时间
 62             Thread.sleep(20000);
 63             break;
 64         case 2:
 65             //初始延时
 66             long initDelay1 = 5000;
 67             //执行周期
 68             long period = 3000;
 69             LOGGER.info("周期性的延时执行Runnable接口 : " + System.currentTimeMillis());
 70             //周期性的执行
 71             scheduledExecutorService.scheduleAtFixedRate(() -> {
 72                 int number = RandomUtils.nextInt(1000, 3000);
 73                 LOGGER.info("周期性的延时执行Runnable接口 [" + number + "]开始运行 : " + System.currentTimeMillis());
 74                 //模拟运行
 75                 try {
 76                     Thread.sleep(2000);
 77                 } catch (InterruptedException e) {
 78                     e.printStackTrace();
 79                 }
 80 //                    LOGGER.info("周期性的延时执行Runnable接口 [" + number + "]结束运行 : " + System.currentTimeMillis());
 81             }, initDelay1, period, TimeUnit.MILLISECONDS);
 82 
 83             //等待多长时间
 84             Thread.sleep(20000);
 85             break;
 86         case 3:
 87             //初始延时
 88             long initDelay2 = 5000;
 89             //执行周期
 90             long period1 = 2000;
 91             LOGGER.info("周期性的延时执行Runnable接口 : " + System.currentTimeMillis());
 92             //周期性的执行
 93             scheduledExecutorService.scheduleAtFixedRate(() -> {
 94                 int number = RandomUtils.nextInt(1000, 3000);
 95                 LOGGER.info("周期性的延时执行Runnable接口 [" + number + "]开始运行 : " + System.currentTimeMillis());
 96                 //模拟运行
 97                 try {
 98                     Thread.sleep(3000);
 99                 } catch (InterruptedException e) {
100                     e.printStackTrace();
101                 }
102                 LOGGER.info("周期性的延时执行Runnable接口 [" + number + "]结束运行 : " + System.currentTimeMillis());
103             }, initDelay2, period1, TimeUnit.MILLISECONDS);
104 
105             //等待多长时间
106             Thread.sleep(20000);
107             break;
108         default:
109             break;
110     }
111     //如果没关闭,则关闭
112     if (!scheduledExecutorService.isShutdown()) {
113         scheduledExecutorService.shutdown();
114     }
115 }
复制代码

type=0时,调用schedule,执行结果:

2018-04-06 16:26:18 INFO - 延时执行Runnable接口 : 1523003178598
2018-04-06 16:26:20 INFO - 2秒之后 : 1523003180644

2018-04-06 16:26:21 INFO - 延时执行Callable接口 : 1523003181144
2018-04-06 16:26:23 INFO - 2秒之后 :1523003183146

无论是Runnable还是Callable任务,都只是执行了一次。


type=1时,调用scheduleWithFixedDelay,执行结果:

2018-04-06 16:30:24 INFO - 周期性的延时执行Runnable接口 : 1523003424210
2018-04-06 16:30:29 INFO - 周期性的延时执行Runnable接口 [1513]开始运行 : 1523003429281
2018-04-06 16:30:31 INFO - 周期性的延时执行Runnable接口 [1513]结束运行 : 1523003431281
2018-04-06 16:30:34 INFO - 周期性的延时执行Runnable接口 [2579]开始运行 : 1523003434282
2018-04-06 16:30:36 INFO - 周期性的延时执行Runnable接口 [2579]结束运行 : 1523003436282
2018-04-06 16:30:39 INFO - 周期性的延时执行Runnable接口 [2347]开始运行 : 1523003439283
2018-04-06 16:30:41 INFO - 周期性的延时执行Runnable接口 [2347]结束运行 : 1523003441284

上一次任务结束 与 下一次任务开始 的间隔 = delay = 3秒


type=2时,调用scheduleAtFixedRate,执行结果:

2018-04-06 16:33:48 INFO - 周期性的延时执行Runnable接口 : 1523003628760
2018-04-06 16:33:53 INFO - 周期性的延时执行Runnable接口 [2601]开始运行 : 1523003633808
2018-04-06 16:33:56 INFO - 周期性的延时执行Runnable接口 [2189]开始运行 : 1523003636804
2018-04-06 16:33:59 INFO - 周期性的延时执行Runnable接口 [2071]开始运行 : 1523003639804
2018-04-06 16:34:02 INFO - 周期性的延时执行Runnable接口 [2399]开始运行 : 1523003642803
2018-04-06 16:34:05 INFO - 周期性的延时执行Runnable接口 [1743]开始运行 : 1523003645803
2018-04-06 16:34:08 INFO - 周期性的延时执行Runnable接口 [2351]开始运行 : 1523003648804

上一次任务开始 与 下一次任务开始 的间隔 = delay = 3秒


type=3时,调用scheduleAtFixedRate,执行结果:

2018-04-06 16:35:04 INFO - 周期性的延时执行Runnable接口 : 1523003704193
2018-04-06 16:35:09 INFO - 周期性的延时执行Runnable接口 [1614]开始运行 : 1523003709250
2018-04-06 16:35:12 INFO - 周期性的延时执行Runnable接口 [1614]结束运行 : 1523003712250
2018-04-06 16:35:12 INFO - 周期性的延时执行Runnable接口 [2846]开始运行 : 1523003712250
2018-04-06 16:35:15 INFO - 周期性的延时执行Runnable接口 [2846]结束运行 : 1523003715251
2018-04-06 16:35:15 INFO - 周期性的延时执行Runnable接口 [2760]开始运行 : 1523003715252
2018-04-06 16:35:18 INFO - 周期性的延时执行Runnable接口 [2760]结束运行 : 1523003718253
2018-04-06 16:35:18 INFO - 周期性的延时执行Runnable接口 [2262]开始运行 : 1523003718253
2018-04-06 16:35:21 INFO - 周期性的延时执行Runnable接口 [2262]结束运行 : 1523003721255
2018-04-06 16:35:21 INFO - 周期性的延时执行Runnable接口 [2565]开始运行 : 1523003721255
2018-04-06 16:35:24 INFO - 周期性的延时执行Runnable接口 [2565]结束运行 : 1523003724256

因为任务的执行时间(3秒)大于开始任务的周期period(2秒),所以:

  • 上一次任务开始 与 下一次任务开始 的间隔时间 = 任务执行时间
  • 上一次任务结束 与 下一次任务开始 的间隔 0 秒


这篇关于Java并发39:Executor系列--ScheduleExecutorService接口学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程