[Multi-threaded programming]--Analysis of task scheduling classes ThreadPoolTaskScheduler and ScheduledThreadPoolExecutor

I. Introduction

Common task scheduling in springboot
This article introduces some common task scheduling technology methods. Next, ThreadPoolTaskScheduler and ScheduledThreadPoolExecutor will be compared. Analyze through simple use, source code implementation, advantages and disadvantages, etc., so that the corresponding technology can be used in various application scenarios.

Note: The technology introduced below is mainly considered for use in non-distributed environments.

Applicable scenarios for ScheduledThreadPoolExecutor: mainly delaying one-time tasks and periodic tasks;
Applicable scenarios for ThreadPoolTaskScheduler: delaying tasks and periodic tasks with timing plans [can be represented by cron expressions];

[Remarks: In a distributed environment, if you require delayed tasks, please refer to the article Implementing Delay Queue Business Scenarios ; if you require periodic tasks/timed tasks, task scheduling @Scheduled, ScheduledThreadPoolExecutor, quartz, xxl-job, please refer to the article's quartz/ xxl-job and other distributed components]

二、ScheduledThreadPoolExecutor

The ScheduledThreadPoolExecutor class mainly relies on ThreadPoolExecutor's delayed blocking queue DelayedWorkQueue
(1) to support the submission of delayed tasks schedule(), periodic tasks scheduleAtFixedRate() or scheduleWithFixedDelay();

2.1. Submit delayed task schedule()

This is mainly a delayed task calling method. ScheduledThreadPoolExecutor.schedule will wrap user tasks into ScheduledFutureTask. ScheduledFutureTask is a subclass of FutureTask, so exceptions are not thrown and are recorded.
schedule() source code is as follows:

scheduler.schedule(new Runnable() {
   
    
    
            @Override
            public void run() {
   
    
    
                //只是延迟,然后执行一次
                System.out.println("addDelayTask start."+ TimeUtil.currentDatetime());
            }
        },10,TimeUnit.SECONDS);

ScheduledThreadPoolExecutor类的
public ScheduledFuture<?> schedule(Runnable command,
                                       long delay,
                                       TimeUnit unit) {
   
    
    
        if (command == null || unit == null)
            throw new NullPointerException();
        //将外部传入的任务封装成延迟任务对象ScheduledFutureTask
        RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Void>(command, null,
                                          triggerTime(delay, unit)));
        delayedExecute(t); //后面介绍
        return t;
    }

triggerTime() calculates the delay time and triggers the execution time of the current task----[current time + delay time]

 private long triggerTime(long delay, TimeUnit unit) {
   
    
    
        return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
    }

2.1.1. DelayedExecute method of delayed task execution

private void delayedExecute(RunnableScheduledFuture<?> task) {
   
    
    
        if (isShutdown())
            reject(task); //如果是被中断,那么按照拒绝策略实行
        else {
   
    
    
            super.getQueue().add(task); //向阻塞队列中添加任务
            //线程池状态为 SHUTDOWN 并且不允许执行任务了,就从队列删除该任务,并设置任务的状态为取消状态
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                ensurePrestart(); //进入ThreadPoolExecutor类逻辑
        }
    }

Entering the ThreadPoolExecutor class, the following is mainly the addWorker() method, which is to add a thread to the thread pool. Returning true indicates that the Worker is created successfully and the thread is started.

void ensurePrestart() {
   
    
    
        int wc = workerCountOf(ctl.get());
        if (wc < corePoolSize)
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }

2.2. Delayed task queue delayedQueue

The previous addWorker() method added the worker thread to the thread pool. According to the thread pool principle, it is blocked in the take() method of obtaining data.
—So, focus on how to obtain tasks in the DelayedWorkQueue queue ?
Please refer to queue queue analysis to explain the principle of delay queue.

2.2.1. DelayedWorkQueue#take() method to obtain delayed tasks

This method mainly obtains tasks in the delay queue whose task delay time is less than or equal to 0.
If the delay time is not less than 0, then call the awaitNanos(delay) blocking method of the condition queue and wait for a period of time. When the time is up, the delay time will naturally be less than or equal to 0.
After obtaining the task, the worker thread can start executing the scheduled task.
-------As can be seen from the above, we encapsulate the task thread into a ScheduledFutureTask object. Then, after getting the data from the delay queue, we start executing the run() method.

2.2.2. ScheduledFutureTask#run() method runs the task

 public void run() {
   
    
    
          //判断任务是否是周期性任务还是非周期性任务
            boolean periodic = isPeriodic(); 
            //是否是周期性任务,且检测当前状态能否执行,不能执行就取消
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic) //非周期任务,直接调用 FutureTask#run 执行一次
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {
   
    
    //周期任务的执行
                setNextRunTime(); //设置周期任务的下一个执行时间
                // 任务的下一次执行安排,如果当前线程池状态可以执行周期任务,加入队列,并开启新线程
                reExecutePeriodic(outerTask);
            }
   }

ScheduledFutureTask.super.runAndReset() executes periodic tasks. After the periodic task is completed normally, the status of the task will not change and is still NEW;
if an exception occurs during the execution of this task, the setException method will be entered to set the task status to abnormal. The task will not be executed periodically;

2.3. Periodic task scheduleAtFixedRate()

おすすめ

転載: blog.csdn.net/xunmengyou1990/article/details/128696050