线程池ScheduledExecutorService使用
Posted 架构师日刊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程池ScheduledExecutorService使用相关的知识,希望对你有一定的参考价值。
1、长期为你提供最优质的学习资源!
2、给你解决技术问题!
4、每周1、3、5送纸质书籍免费送给大家,每年至少送书800本书!
5、为大家推荐靠谱的就业单位!
请注意!我上面说的5点全部都是免费的!全网你应该找不到第二家吧!
当然,大家在我私人微信上问我问题,仅限回答web前端、java相关的。
---------------------------
好了,接下来开始今天的技术分享!上次老师跟大家分享了线程池ThreadPoolExecutor类使用的知识,今天跟大家分享下线程池ScheduledExecutorService使用的知识。
1 Executor 结构图
2 ScheduledExecutorService是什么?
ScheduledExecutorService接口是netty事件循环组(eventLoop)实现的顶级接口,因此需要对该接口有较为深入的理解才能较为更好的理解netty的事件循环组。ScheduledExecutorService接口是java线程池中最重要的几个接口之一。它除了支持原生线程池的功能之外,同时支持定时任务处理的功能。
public interface ScheduledExecutorService extends ExecutorService 可安排在给定的延迟后运行或定期执行的命令。
schedule 方法使用各种延迟创建任务,并返回一个可用于取消或检查执行的任务对象
scheduleAtFixedRate 和 scheduleWithFixedDelay 方法创建并执行某些在取消前一直定期运行的任务
所有的 schedule 方法都接受相对延迟和周期作为参数,而不是绝对的时间或日期
SheduleExecutorService 是JDK 1.5出来的,比以前的Timer性能好
在JDK中为它提供了一个默认的实现类:ScheduledThreadPoolExecutor。下面我们来看它的基本用法。
3 ScheduledExecutorService的使用
ScheduledExecutorService包括三个方法:schedule()、scheduleAtFixedRate()、scheduleWithFixedDelay()。下面主要分析ScheduledThreadPoolExecutor实现类的使用。
schedule方法的实现原理
scheduleAtFixedRate()和scheduleWithFixedDelay()实现原理
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);
}
输出:
第一次提交
第二次提交
3114
7115
例子2:
public 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);
}
输出:
第一次提交
第二次提交
3167
3169
例子3
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("第二次提交");
// 注意此处延迟时间改为2s
executorService.schedule(()->{
System.out.println(System.currentTimeMillis() - start);
}, 2, TimeUnit.SECONDS);
}
输出:
第一次提交
第二次提交
2103
3100
结果分析:
1.例子1中,线程池大小1,第一次提交的任务成功延迟3s执行,并且执行耗时为4s。而第二次提交的任务延迟7s后才执行,不符合3s延迟的预期。
2.例子2中,线程池大小2,第一次和第二次提交的任务都延迟3s执行,符合预期。说明当有多个任务提交的时候,延迟执行与线程池的大小和上一个任务执行耗时两个因素有关。
3.例子3中,线程池大小1,第一次提交的任务延迟3s执行,第二次提交的任务延迟2s执行。说明提交任务的先后顺序与实际执行的顺序无关。
结论:起到延迟执行的作用;多次提交任务时,后面任务延迟执行的时间是否准确,与线程池的大小和上一个任务执行耗时两个因素有关。提交任务的先后顺序与实际执行的顺序无关,而是与延迟时间有关。
2.scheduleAtFixedRate()方法
scheduleAtFixedRate()方法比起前面的schedule()方法复杂得多,这里就不再分析提交多个任务的情况,等到讲解了他们的实现原理之后,再根据原理分析即可。同样先演示其基本用法,再根据输出分析结论。
例子1
public 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);
}
例子2
public 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()方法
例子1
public 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);
}
例子2
public 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使用的主要内容,如果未能解决你的问题,请参考以下文章