preface

Characteristics that

1. Constructor

 public ScheduledThreadPoolExecutor(int corePoolSize) {
 The other parameters are already parsed in ThreadPoolExecutor, so they will not be expanded here
 // We can see that the method in the base class has a special input parameter DelayedWorkQueue.
 // We can also see that there is no set delay time, period and other parameters entry.
 // So the timed implementation must be in the DelayedWorkQueue object.
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
Copy the code

2.DelayedWorkQueue





private RunnableScheduledFuture<? >[] queue = new RunnableScheduledFuture<? >[INITIAL_CAPACITY];Copy the code

We analyzed ThreadPoolExecutor, it get task from the task queue to poll and take two kinds, so take a look at the poll and take two methods of source code, in retrospect, ThreadPoolExecutor it calls the poll or take method, first poll, Take again, as long as one of the interfaces returns

public RunnableScheduledFuture<? >poll() { final ReentrantLock lock = this.lock; lock.lock(); try { RunnableScheduledFuture<? > first = queue[0]; // There is a getDelay here, which is the key point, to get the execution delay // but if we have a delay setting, this returns nothing, and then we call the take methodif (first == null || first.getDelay(NANOSECONDS) > 0)
                    return null;
                else
                    returnfinishPoll(first); } finally { lock.unlock(); } } public RunnableScheduledFuture<? > take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try {for(;;) { RunnableScheduledFuture<? > first = queue[0];if (first == null)
                        available.await();
                    elseLong delay = first.getdelay (NANOSECONDS);if (delay <= 0)
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting if (leader ! = null) available.await(); else { Thread thisThread = Thread.currentThread(); leader = thisThread; Try {// Use the lock to perform a delay wait. // Use the lock to execute the delay wait. // Use the lock to execute the delay wait. available.awaitNanos(delay); } finally { if (leader == thisThread) leader = null; } } } } } finally { if (leader == null && queue[0] ! = null) available.signal(); lock.unlock(); }}Copy the code

3.RunnableScheduledFuture 

Have a ScheduledFutureTask within ScheduledThreadPoolExecutor class implements the RunnableScheduledFuture, ScheduledFutureTask this class adopted the decorator design pattern, Some additional functionality is performed on top of the Runnable method. We need to pay special attention to the parameters period and time.

(1) Time First look at the role of time, it can be found that time is used to obtain the execution delay time, that is, delay is generated according to time

public long getDelay(TimeUnit unit) {
            return unit.convert(time - now(), NANOSECONDS);
        }
Copy the code

(2) The period parameter is not used to set the number of execution periods. It is used to determine whether the execution period is required and the execution period, that is, the interval between the current execution and the next execution

If the interval is set to 0, execute only once instead of every interval, pay special attention to public BooleanisPeriodic() {
            returnperiod ! = 0; }Copy the code

 private void setNextRunTime() {
            long p = period;
            if(p > 0) // Add the period to time, so the delay is the period time. time += p;else
                time = triggerTime(-p);
        }
Copy the code

(3) Execution

 public void runBoolean periodic = isPeriodic(); Boolean periodic = isPeriodic();if(! canRunInCurrentRunState(periodic)) cancel(false);
            else if(! Periodic) // If it is not periodic, the run method of the parent class is called, which is the run method of the Runnable object passed in the constructor. ScheduledFutureTask.super.run(); / / inifThe task is executed first in parentheseselse if(ScheduledFutureTask. Super. RunAndReset ()) {/ / if they are cyclical, you need to set up the next execution time, and then use reExecutePeriodic method, cast back into the task queue. // This is especially importantifIf no exception is caught, then the following logic will not be executed, that is to say, there is a failure in the middle of the execution, the following periodic task is invalid.setNextRunTime(); reExecutePeriodic(outerTask); }}Copy the code

conclusion

ScheduledThreadPoolExecutor through time parameters, set the current task waiting time, and then through setting task execution needs to wait for next time period of time. Neither of these parameters is set in the thread pool, but carried in the task, which completely decouples the thread pool from the task.

Note:

(1) The execution waiting time of the task is in the take method of the queue.

(2) If the period parameter is set to 0, the task will only be executed once, not multiple times

(3) If you want to implement periodic tasks by yourself, you must pay attention to catch exceptions during the execution of periodic tasks. Otherwise, if one execution fails, the subsequent Task cycles will become invalid and the Task will no longer be executed.