Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别
Posted 请叫我大师兄_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别相关的知识,希望对你有一定的参考价值。
Java实现定时任务,一般都是用一个线程,设置个时间,让他定时执行,注意力一般都是集中在这个线程的实现,很少考虑到具体定时执行线程的这个过程。scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别大吗?要是不了解的话,还真会因为不了解两者的差别导致一些线上bug呢!
正规的做法,咱要使用线程,就得正规的使用线程池来实现线程的调用,而不能图一时之快,手动的弄个线程就给他start了,这么操作是不妥当,坚决反对。而创建线程池又有讲究,期望自己手动设置一些参数,从而了解和牢记线程池实现原理。
定时任务线程池提供类
public final class MonitorService
private static final int CORE_POOL_SIZE = 10;
private static final int DELAY = 60;
private static volatile ScheduledExecutorService monitorExecutor;
public static ScheduledExecutorService getMonitorExecutor()
if (monitorExecutor == null)
synchronized (MonitorService.class)
if (monitorExecutor == null)
ThreadFactory monitorThreadFactory = new ThreadFactoryBuilder().setNameFormat("monitorThreadPool-%d").build();
monitorExecutor = new ScheduledThreadPoolExecutor(CORE_POOL_SIZE, monitorThreadFactory, new ThreadPoolExecutor.AbortPolicy());
return monitorExecutor;
问题0:这个线程池的maximumPoolSize是多少?
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler)
问题1:提交的任务超过 corePoolSize 会怎么样?
@Test
public void test() throws InterruptedException
for (int i = 0; i < 100; i++)
int finalI = i;
MonitorService.getMonitorExecutor().scheduleWithFixedDelay(()->
System.out.println(finalI + " ");
try
TimeUnit.MINUTES.sleep(5);
catch (Exception ignore)
, 5, 5, TimeUnit.SECONDS);
System.out.println("主线程运行到此。");
TimeUnit.MINUTES.sleep(5);
运行结果截图:
结论:
他首先会把多余的给放到缓存队列中,队列放不下了,才会启动大于core size 个的线程去执行任务,当超过maximumPoolSize 的时候,执行拒绝策略。
问题2:上面的情况为什么不直接new线程去执行任务,而是把任务丢到队列里等着?
就好比一个国企里面有10个(core)正式工的名额,最多招10个正式工,要是任务超过正式工人数(task > core)的情况下,工厂领导(线程池)不是首先扩招工人,还是这10人,但是任务可以稍微积压一下,即先放到队列去(代价低)。10个正式工慢慢干,迟早会干完的,要是任务还在继续增加,超过正式工的加班忍耐极限了(队列满了),就的招临时工帮忙了(注意是临时工)要是正式工加上临时工还是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。
为啥老板不上来直接招正式工?
首先,招人(new thread)是不是得走流程,办手续,耗时耗力;
其次,要是任务量降低了,养多出来的几个人没活干成本高(维护更多的线程的成本);
最后,要辞退临时工也得各种走流程,各种赔偿,得不偿失。(超时清掉产能过剩的线程的成本)。
正式工(线程)相对于计算机世界,是稀缺资源。国企的铁饭碗可不就是稀缺资源嘛,不到不得已,是不能泛滥的。
问题3:两个定时任务的实现的差别?
1,先通过实际代码看scheduleWithFixedDelay的实际运行效果。
@Test
public void with() throws InterruptedException
System.out.println("当前是:" + TimeUtils.now());
MonitorService.getMonitorExecutor().scheduleWithFixedDelay(() ->
try
System.out.println("线程 run 了。。。。。。" + TimeUtils.nowS());
TimeUnit.SECONDS.sleep(5);
System.out.println("线程 sleep 完了。。。。。。" + TimeUtils.nowS());
catch (InterruptedException e)
e.printStackTrace();
, 1, 1, TimeUnit.SECONDS);
System.out.println("主线程运行到此。当前是:" + TimeUtils.now());
TimeUnit.MINUTES.sleep(500);
运行结果截图:
定时任务执行过程中睡了5s,41开始,46结束,下次47的时候开始,我们设置的执行频率是1s一执行,但是,实际执行频率是6s一次。
理论:
scheduleWithFixedDelay 指的是“以固定的延时”执行,delay(延时)指的是一次执行终止和下一次执行开始之间的延迟。本test现象:执行完歇息1秒,再下次执行 。
痛点:run的执行多少总要耽误点时间的,即使是毫秒级别的,但是,执行的次数多了,这个定时任务的延迟频率会越来越大。
2,再通过实际代码看scheduleAtFixedRate的实际运行效果。
@Test
public void at() throws InterruptedException
System.out.println("当前是:" + TimeUtils.now());
MonitorService.getMonitorExecutor().scheduleAtFixedRate(() ->
try
System.out.println("线程 run 了。。。。。。" + TimeUtils.nowS());
TimeUnit.SECONDS.sleep(5);
System.out.println("线程 sleep 完了。。。。。。" + TimeUtils.nowS());
catch (InterruptedException e)
e.printStackTrace();
, 1, 1, TimeUnit.SECONDS);
System.out.println("主线程运行到此。当前是:" + TimeUtils.now());
TimeUnit.MINUTES.sleep(500);
运行结果截图:
96开始执行,睡5s,01结束,然后,下一次也在01的时候就开始了
理论:
scheduleAtFixedRate 指的是“以固定的频率”执行,period(周期)指的是两次成功执行之间的时间。 上一个任务开始的时间计时,一个period后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。FixedRate: 固定比率 本test现象:执行完,没有歇息,直接开始下一次。
这个是尽力去实现那个间隔,除非run很耽误时间,run的时间超过了设置的执行间隔,才会延迟,with的,即使run时间短,他也是在一点点的延迟的,这个就不会。
以上是关于Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别的主要内容,如果未能解决你的问题,请参考以下文章
Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别