ScheduledThreadPoolExecutor实现原理

ScheduledThreadPoolExecutor运作在ThreadPoolExecutor的基础上,自定义实现任务队列,每次take()拉取任务的时候,返回运行优先级最高的队首元素。

构造函数:将自定义的阻塞队列作为参数放到ScheduledThreadPoolExecutor里面。

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

源代码部分(take部分):

public RunnableScheduledFuture<?> take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                for (;;) {
                    RunnableScheduledFuture<?> first = queue[0];
                    if (first == null)
                        available.await();
                    else {
                        long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0)
                            //返回任务
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting
                        if (leader != null)
                            //如果有线程已经等待执行了,那么自己就await
                            available.await();
                        else {
                            //只能有一个线程延迟等待
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                //线程延迟等待
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && queue[0] != null)
                    available.signal();
                lock.unlock();
            }
        }

这里的任务的获取是采用小根堆的算法,也就是一次排序能保证根节点的优先级最高,这里的delay越小,表示任务的运行优先级越高,如果小于0,那么这个任务表示已经过了执行时间,所以优先级最高,必须马上执行;如果优先级最高的任务的delay大于0,也就是任务的执行时间还没到,那么线程休眠delay时间。这里有一个leader进程,也就保证了只能一个线程去延迟等待,如果还有其他需要延迟的进程,那么就只能await等待。

在任务的执行函数体(run方法)里面,如果检测到时周期任务,那么将会再次设置下次的执行时间,将这个任务再次加到队列里面排队。

源代码:

public void run() {
            boolean periodic = isPeriodic();
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {
                setNextRunTime();
                reExecutePeriodic(outerTask);
            }
        }

void reExecutePeriodic(RunnableScheduledFuture<?> task) {
        if (canRunInCurrentRunState(true)) {
            super.getQueue().add(task);
            if (!canRunInCurrentRunState(true) && remove(task))
                task.cancel(false);
            else
                ensurePrestart();
        }
    }

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

time表示下次执行时间,这里可以看出来,如果一个定时任务时每两秒执行一次,如果在此期间有优先级更高的任务堆积运行,那么当运行到该定时任务的时候,会很快执行,并不会与上一次执行相差两秒。

演示代码:

public static void main(String[] args) {
		ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
		for (int i = 0; i < 30; i++) {
			final int index = i;
			if (i == 0) {
				executor.scheduleAtFixedRate(()->
						System.out.println("现在时间 : " + System.currentTimeMillis() + " , 我在运行.")
				,0,2, TimeUnit.SECONDS);
			} else {
				executor.execute(()->{
					System.out.println("我不是延迟任务,我优先级最高,我是任务" + index);
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				});
			}
		}
	}

结果:

现在时间 : 1547182453546 , 我在运行.
我不是延迟任务,我优先级最高,我是任务1
我不是延迟任务,我优先级最高,我是任务2
我不是延迟任务,我优先级最高,我是任务3
我不是延迟任务,我优先级最高,我是任务4
我不是延迟任务,我优先级最高,我是任务5
我不是延迟任务,我优先级最高,我是任务6
我不是延迟任务,我优先级最高,我是任务7
我不是延迟任务,我优先级最高,我是任务8
我不是延迟任务,我优先级最高,我是任务9
我不是延迟任务,我优先级最高,我是任务10
我不是延迟任务,我优先级最高,我是任务11
我不是延迟任务,我优先级最高,我是任务12
我不是延迟任务,我优先级最高,我是任务13
我不是延迟任务,我优先级最高,我是任务14
我不是延迟任务,我优先级最高,我是任务15
我不是延迟任务,我优先级最高,我是任务16
我不是延迟任务,我优先级最高,我是任务17
我不是延迟任务,我优先级最高,我是任务18
我不是延迟任务,我优先级最高,我是任务19
我不是延迟任务,我优先级最高,我是任务20
我不是延迟任务,我优先级最高,我是任务21
我不是延迟任务,我优先级最高,我是任务22
我不是延迟任务,我优先级最高,我是任务23
我不是延迟任务,我优先级最高,我是任务24
我不是延迟任务,我优先级最高,我是任务25
我不是延迟任务,我优先级最高,我是任务26
我不是延迟任务,我优先级最高,我是任务27
我不是延迟任务,我优先级最高,我是任务28
我不是延迟任务,我优先级最高,我是任务29
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182468113 , 我在运行.
现在时间 : 1547182469540 , 我在运行.
现在时间 : 1547182471541 , 我在运行.

结果表明:每次运行完定时任务,设置的时间是上一次的time+period,所以如果上一次的时间加上延迟的时间还不能赶上现在的时间,那么这个任务还是看作是执行耽误的任务,将马上运行,所以就出现了上述的短时间内重复运行周期任务,直到赶上现在的时间,才会有延迟产生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ScheduledThreadPoolExecutor 是 Java 中的一个线程池实现,它可以根据指定的延迟时间或定时周期执行任务。其原理主要包括以下几个方面: 1. 线程池管理:ScheduledThreadPoolExecutor 内部维护一个线程池,其中包含多个工作线程。这些工作线程可以并行执行提交的任务。 2. 任务队列:ScheduledThreadPoolExecutor 使用一个任务队列来存储提交的任务。当任务被提交时,会先进入任务队列中等待执行。 3. 任务调度:ScheduledThreadPoolExecutor 使用一个调度器来控制任务的执行时间。调度器会根据任务的延迟时间或定时周期,将任务从任务队列中取出,并分配给空闲的工作线程执行。 4. 线程池调度策略:ScheduledThreadPoolExecutor 提供了不同的调度策略,可以根据需要选择合适的策略。见的调度策略包括延迟执行、周期性执行、固定延迟执行等。 5. 线程池管理和任务调度的协调:ScheduledThreadPoolExecutor 通过线程池管理和任务调度的协调,实现了对延迟执行或定时周期执行任务的支持。它会根据需要动态地创建、销毁工作线程,并将任务分配给这些工作线程执行。 总之,ScheduledThreadPoolExecutor 通过线程池管理和任务调度的机制,实现了对延迟执行或定时周期执行任务的支持。这使得开发者可以方便地控制任务的执行时间,并且可以充分利用系统资源来提高任务执行的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值