线程池ScheduledExecutorService使用

Posted 架构师日刊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程池ScheduledExecutorService使用相关的知识,希望对你有一定的参考价值。


    阅读本文约需要5分钟 

1、长期为你提供最优质的学习资源!

2、给你解决技术问题!

4、每周1、3、5送纸质书籍免费送给大家,每年至少送书800本书!

5、为大家推荐靠谱的就业单位!

请注意!我上面说的5点全部都是免费的!全网你应该找不到第二家吧!

当然,大家在我私人微信上问我问题,仅限回答web前端、java相关的。

---------------------------

好了,接下来开始今天的技术分享!上次老师跟大家分享了线程池ThreadPoolExecutor类使用的知识,今天跟大家分享下线程池ScheduledExecutorService使用的知识。

Executor 结构图

线程池ScheduledExecutorService使用

ScheduledExecutorService是什么?

ScheduledExecutorService接口是netty事件循环组(eventLoop)实现的顶级接口,因此需要对该接口有较为深入的理解才能较为更好的理解netty的事件循环组。ScheduledExecutorService接口是java线程池中最重要的几个接口之一。它除了支持原生线程池的功能之外,同时支持定时任务处理的功能。

public interface ScheduledExecutorService extends ExecutorService 可安排在给定的延迟后运行或定期执行的命令。schedule 方法使用各种延迟创建任务,并返回一个可用于取消或检查执行的任务对象scheduleAtFixedRatescheduleWithFixedDelay 方法创建并执行某些在取消前一直定期运行的任务所有的 schedule 方法都接受相对延迟和周期作为参数,而不是绝对的时间或日期SheduleExecutorServiceJDK 1.5出来的,比以前的Timer性能好

线程池ScheduledExecutorService使用

在JDK中为它提供了一个默认的实现类:ScheduledThreadPoolExecutor。下面我们来看它的基本用法。

ScheduledExecutorService的使用

ScheduledExecutorService包括三个方法:schedule()、scheduleAtFixedRate()、scheduleWithFixedDelay()。下面主要分析ScheduledThreadPoolExecutor实现类的使用。

schedule方法的实现原理

线程池ScheduledExecutorService使用

scheduleAtFixedRate()和scheduleWithFixedDelay()实现原理线程池ScheduledExecutorService使用

1.schedule()方法

下面将演示该方法的基本使用,以及通过实验得出的结论,具体的实现原理后面分析。先来看几个例子。

例子1

public static void main(String[] args) { // 注意此处线程个数为1 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); long start = System.currentTimeMillis(); System.out.println("第一次提交"); executorService.schedule(()->{ System.out.println(System.currentTimeMillis() - start); try { // 注意此处休眠4秒 Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } }, 3, TimeUnit.SECONDS); System.out.println("第二次提交"); executorService.schedule(()->{ System.out.println(System.currentTimeMillis() - start); }, 3, TimeUnit.SECONDS);}
输出:第一次提交第二次提交31147115
例子2public static void main(String[] args) { // 相较于例子1,这里的线程池改为2 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); long start = System.currentTimeMillis(); System.out.println("第一次提交"); executorService.schedule(()->{ System.out.println(System.currentTimeMillis() - start); try { // 注意此处休眠4秒 Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } }, 3, TimeUnit.SECONDS); System.out.println("第二次提交"); executorService.schedule(()->{ System.out.println(System.currentTimeMillis() - start); }, 3, TimeUnit.SECONDS);}
输出:第一次提交第二次提交31673169
例子3public static void main(String[] args) { // 注意这里的线程池个数改为1了 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); long start = System.currentTimeMillis(); System.out.println("第一次提交"); executorService.schedule(()->{ System.out.println(System.currentTimeMillis() - start); try { // 注意此处休眠4秒 Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } }, 3, TimeUnit.SECONDS); System.out.println("第二次提交"); // 注意此处延迟时间改为2s executorService.schedule(()->{ System.out.println(System.currentTimeMillis() - start); }, 2, TimeUnit.SECONDS);}
输出:第一次提交第二次提交21033100

结果分析:

1.例子1中,线程池大小1,第一次提交的任务成功延迟3s执行,并且执行耗时为4s。而第二次提交的任务延迟7s后才执行,不符合3s延迟的预期。

2.例子2中,线程池大小2,第一次和第二次提交的任务都延迟3s执行,符合预期。说明当有多个任务提交的时候,延迟执行与线程池的大小和上一个任务执行耗时两个因素有关。

3.例子3中,线程池大小1,第一次提交的任务延迟3s执行,第二次提交的任务延迟2s执行。说明提交任务的先后顺序与实际执行的顺序无关。

结论:起到延迟执行的作用;多次提交任务时,后面任务延迟执行的时间是否准确,与线程池的大小和上一个任务执行耗时两个因素有关。提交任务的先后顺序与实际执行的顺序无关,而是与延迟时间有关。

2.scheduleAtFixedRate()方法

scheduleAtFixedRate()方法比起前面的schedule()方法复杂得多,这里就不再分析提交多个任务的情况,等到讲解了他们的实现原理之后,再根据原理分析即可。同样先演示其基本用法,再根据输出分析结论。

例子1public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.scheduleAtFixedRate(()->{ System.out.println("coming"); try { // 注意此处休眠时间为2s Thread.sleep(2000); System.out.println("sleep end"); } catch (InterruptedException e) { e.printStackTrace(); } // 延迟0s执行,周期为3s }, 0, 3, TimeUnit.SECONDS);}
例子2public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.scheduleAtFixedRate(()->{ System.out.println("coming"); try { // 注意此处休眠时间为5s Thread.sleep(5000); System.out.println("sleep end"); } catch (InterruptedException e) { e.printStackTrace(); } // 延迟0s执行,周期为3s }, 0, 3, TimeUnit.SECONDS);}

先到IDE跑这两个例子,我们可以发现。

结果分析:例子1中,任务正常的每3s周期性执行;例子2中,每个任务执行耗时为5s,而我们预期的是周期3s执行一次。但事实上是需要等待上一个周期执行完毕,下一个周期马上执行。也就是5s执行一次。

结论:此方法用于周期性执行任务。当任务耗时长于周期,那么下一个周期任务将在上一个执行完毕之后马上执行。当任务耗时短于周期,那么正常周期性执行。

3.scheduleWithFixedDelay()方法

例子1public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.scheduleWithFixedDelay(()->{ System.out.println("coming"); try { // 注意此处休眠时间为2s Thread.sleep(2000); System.out.println("sleep end"); } catch (InterruptedException e) { e.printStackTrace(); } // 第一个任务延迟0s执行,其余延迟为3s }, 0, 3, TimeUnit.SECONDS);}
例子2public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.scheduleWithFixedDelay(()->{ System.out.println("coming"); try { // 注意此处休眠时间为5s Thread.sleep(5000); System.out.println("sleep end"); } catch (InterruptedException e) { e.printStackTrace(); } // 第一个任务延迟0s执行,其余延迟为3s }, 0, 3, TimeUnit.SECONDS);}


到IDE跑这两个例子。

结果分析:例子1中,第二次执行等到上一次执行完毕之后,延迟3s才执行。例子2中,也是一样。

结论:此方法用于周期性执行;无论上一个方法耗时多长,下一个方法都会等到上一个方法执行完毕之后,再经过delay的时间才执行。

今天就分享这么多,关于线程池ScheduledExecutorService使用,你学会了多少欢迎在留言区评论,对于有价值的留言,我们都会一一回复的。如果觉得文章对你有一丢丢帮助,请点右下角【在看】,让更多人看到该文章。




以上是关于线程池ScheduledExecutorService使用的主要内容,如果未能解决你的问题,请参考以下文章

什么叫线程池?线程池如何使用?

多线程(六):线程池

多线程(六):线程池

十五、线程池(六)自动创建线程池的弊端

java 如何获得线程池中正在执行的线程数?

线程池参数与线程池调优