ScheduledThreadPoolExecutor线程池介绍

ThreadPoolExecutory与ScheduledThreadPoolExecutor对比

一个线程池必不可少两大部分,一个存放线程的容器(HashSet),一个存放任务的队列(Queue

ThreadPoolExecutor

1.由于工作队列是多线程共享,也就是说线程容器中的线程都需要访问,所以这个队列必须是线程安全的
2.由于工作线程在没有任务执行的时候,那么需要阻塞,所以,这个队列要支持没有任务的时候让线程阻塞

综上得知,BlockingQueue满足条件

ScheduledThreadPoolExecutor

1.由于线程池需要任务调度,且需要延迟执行,那么推出任务必须能够排队执行,也就是说延迟2s执行的任务就算是最后放入的,也要比延迟5s执行的任务先执行,需要队列能够进行排序。
2.由于放入的任务,不一定能够立即执行,所以还是需要得放入队列,然后获取,看看是否满足执行条件,时间是否满足

结论

1.ThreadPoolExecutor原有部分逻辑不满足此需求,所以需要增加自己的执行逻辑
2.由于任务需要排队,需要能够排序,那么需要实现自己的BlockingQueue

简单使用的区别

scheduleAtFixedRate()

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {

command        任务
initialDelay     刚开始延迟多长时间执行 单位由第四个参数决定
period          每隔多长时间执行
unit         单位

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.scheduleAtFixedRate(() -> {
    System.out.println(LocalDateTime.now() + " -> 1");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, 0, 2, TimeUnit.SECONDS);

任务分析
可以看出每次执行间隔时间是由每次任务执行最大的时间决定的

scheduleWithFixedDelay()

executor.scheduleWithFixedDelay(() -> {
    System.out.println(LocalDateTime.now() + "2");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, 0, 2, TimeUnit.SECONDS);

执行结果
可以看出每次任务执行间隔时间是由执行任务的时间加设置的周期时间决定的

提交任务三大方式

1.schedule()
2.scheduleAtFixedRate()
3.scheduleWithFixedDelay()

我们对比了下这三个方法,里面都用到了decorateTask(),接口返回结果是RunnableScheduledFuture接口,实现类是ScheduledFutureTask

RunnableScheduledFuture接口

public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, ScheduledFuture<V> {
	// 判断是否是周期性任务
    boolean isPeriodic();
}

ScheduledFutureTask实现类

主要成员变量
private class ScheduledFutureTask<V>
        extends FutureTask<V> implements RunnableScheduledFuture<V> {

    // 序列号
    private final long sequenceNumber;

    // 任务开始的时间
    private long time;

    // 任务执行的间隔时间
    private final long period;
run方法介绍
public void run() {
   // 判断是否是周期任务
   boolean periodic = isPeriodic();
   // 判断是否能执行当前任务
   if (!canRunInCurrentRunState(periodic))
       cancel(false);
   // 不是周期任务执行父类的run方法
   else if (!periodic)
       ScheduledFutureTask.super.run();
   else if (ScheduledFutureTask.super.runAndReset()) {
   	   // 设置下次的执行时间
       setNextRunTime();
       // 重复执行任务
       reExecutePeriodic(outerTask);
   }
}

1.如果当前线程池运行状态不可以执行任务,取消该任务,然后直接返回
2.如果不是周期性任务,调用FutureTask中的run方法执行,会设置执行结果,然后直接返回
3.如果是周期性任务,调用FutureTask中的runAndReset方法执行,不会设置执行结果,然后直接返回
4.计算下次执行该任务的具体时间;
5.重复执行任务。

setNextRunTime介绍
private void setNextRunTime() {
     long p = period;
     if (p > 0)
         time += p;
     else
         time = triggerTime(-p);
 }

schedule介绍

public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                        long delay,
                                        TimeUnit unit) {
     // 校验参数
     if (callable == null || unit == null)
         throw new NullPointerException();
     // 装饰任务
     RunnableScheduledFuture<V> t = decorateTask(callable,
         new ScheduledFutureTask<V>(callable,
                                    triggerTime(delay, unit)));
     // 执行任务
     delayedExecute(t);
     return t;
 }

delayedExecute介绍

private void delayedExecute(RunnableScheduledFuture<?> task) {
	// 判断线程池是否关闭
    if (isShutdown())
        reject(task);
    else {
    	// 添加任务到队列
        super.getQueue().add(task);
        // 如果当前任务不能执行执行,则进行取消
        if (isShutdown() && // 线程池关闭
        	// 线程池不能执行任务
            !canRunInCurrentRunState(task.isPeriodic()) &&
            // 删除任务
            remove(task))
            task.cancel(false);
        else
        	// 这里是增加一个worker线程,避免提交的任务没有worker去执行
        	// 原因就是该类没有像ThreadPoolExecutor一样,woker满了才放入队列
            ensurePrestart();
    }
}

scheduleAtFixedRate与scheduleWithFixedDelay任务执行时间间隔差异分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
period一个传的正数,一个传的负数,执行任务的时候会调用setNextTime方法
在这里插入图片描述

这样就清楚一个取最大时间,一个取任务执行时间+设置的周期时间的原因了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值