本章主要学习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); } }
下面对Executor、ExecutorService和ScheduleExecutorService三个接口的特性进行简单的说明:
ScheduleExecutorService的延时调度、周期调度相关方法如下:
1.schedule(Runnable command,long delay, TimeUnit unit)
2.schedule(Callable callable,long delay, TimeUnit unit)
3.scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)
4.scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
练习目的:掌握延时调度任务和周期调度任务的方法的用法。
练习内容:
实例代码:
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秒),所以: