The high concurrency - depth analysis ScheduledFutureTask source category

When provided JDK 1.5 ScheduledThreadPoolExecutor execution timing tasks, the task will be packaged as ScheduledFutureTask objects. So, what is special about ScheduledFutureTask objects? Today, we take a source Shredded ScheduledFutureTask like to deeply understand the details of ScheduledFutureTask class.

Class hierarchy

ScheduledFutureTask can be seen from the definition of the class, the class ScheduledFutureTask private inner class ScheduledThreadPoolExecutor class inherits FutureTask class, and implements the interface RunnableScheduledFuture. In other words, ScheduledFutureTask has all the features FutureTask class and implements all the methods RunnableScheduledFuture interface. The definitions of the following classes ScheduledFutureTask.

private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V>

We can from the hierarchy diagram ScheduledFutureTask class, more clearly see ScheduledFutureTask class inherits which classes and which implements the interface shown, hierarchical relationship diagram of the ScheduledFutureTask class is as follows.

Here Insert Picture Description

Next, we disassemble source ScheduledFutureTask class, explore its details. We also took some of the problems to interpret the source ScheduledFutureTask class, for example, why the package task to ScheduledFutureTask objects? In the regular task, how to distinguish between different ScheduledFutureTask task?

Important member variables

ScheduledFutureTask class member variables are provided as follows.

//任务添加到ScheduledThreadPoolExecutor中被分配的唯一序列号
private final long sequenceNumber;
//下次执行任务的时间
private long time;
//任务执行的周期
private final long period;
//ScheduledFutureTask对象,实际指向当前对象本身
RunnableScheduledFuture<V> outerTask = this;
//当前任务在延迟队列中的索引
//能够更加方便的取消当前任务
int heapIndex;

We look at a few important attributes.

  • sequenceNumber: task to ScheduledThreadPoolExecutor unique serial number is assigned, the task may be determined in accordance with a unique serial number, if the scheduled task, if a task is performed periodically, but they have the same value of sequenceNumber, were regarded as one and the same task.
  • Time to perform tasks at once: time.
  • period: the execution cycle of the task.
  • outerTask: ScheduledFutureTask objects, points to the current actual object itself. Refer to this object reExecutePeriodic method will be passed to periodically perform a task ScheduledThreadPoolExecutor class.
  • heapIndex: the current task in the delay queue index, the index can be more convenient to cancel the current task.

Construction method

ScheduledFutureTask class inherits FutureTask class, and implements RunnableScheduledFuture interface. Providing a configuration method ScheduledFutureTask class.

ScheduledFutureTask(Runnable r, V result, long ns) {
	super(r, result);
	this.time = ns;
	this.period = 0;
	this.sequenceNumber = sequencer.getAndIncrement();
}

ScheduledFutureTask(Runnable r, V result, long ns, long period) {
	super(r, result);
	this.time = ns;
	this.period = period;
	this.sequenceNumber = sequencer.getAndIncrement();
}

ScheduledFutureTask(Callable<V> callable, long ns) {
	super(callable);
	this.time = ns;
	this.period = 0;
	this.sequenceNumber = sequencer.getAndIncrement();
}

Can be seen through the source code in the constructor ScheduledFutureTask class, will first call the constructor FutureTask class member variables for the callable and state FutureTask class assignment, the assignment for the next time ScheduledFutureTask class, period and sequenceNumber member variables. Relatively simple to understand.

getDelay method

Let's look at getDelay method of source code, as shown below.

//获取下次执行任务的时间距离当前时间的纳秒数
public long getDelay(TimeUnit unit) {
	return unit.convert(time - now(), NANOSECONDS);
}

getDelay method is relatively simple, mainly used to obtain the next task execution time from the current system time of a few nanoseconds.

compareTo method

ScheduledFutureTask class structure class implements Comparable interface, compareTo method is to achieve a method for compareTo Comparable interface defined. Source as shown in FIG.

public int compareTo(Delayed other) {
	if (other == this) 
		return 0;
	if (other instanceof ScheduledFutureTask) {
		ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
		long diff = time - x.time;
		if (diff < 0)
			return -1;
		else if (diff > 0)
			return 1;
		else if (sequenceNumber < x.sequenceNumber)
			return -1;
		else
			return 1;
	}
	long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
	return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}

This code looks like a comparison of various types of numeric data, the delay is essentially the task queue sorted. Collation: first comparison time the next execution task queue for each delay, the next task execution time shorter distance to the top surface of time; If the next time the same tasks, the task will sequenceNumber comparison value, sequenceNumber small value tasks standing in the front.

isPeriodic way

The method shown isPeriodic The source code.

//判断是否是周期性任务
public boolean isPeriodic() {
	return period != 0;
}

The role of this method is relatively simple, is mainly used to determine if the current task is a periodic task. Here the task execution cycle as long as the determination operation is not equal to 0 can be determined as a periodic task. Because the period regardless of the value 0 is greater or smaller than 0, the current task is a periodic task.

setNextRunTime method

The method of action is mainly setNextRunTime disposed next time the current task is executed, source code is shown below.

private void setNextRunTime() {
	long p = period;
	//固定频率,上次执行任务的时间加上任务的执行周期
	if (p > 0)
		time += p;
	//相对固定的延迟执行,当前系统时间加上任务的执行周期
	else
		time = triggerTime(-p);
}

Here, once again it proved using isPeriodic method of determining whether or not the current task is a periodic task when, as long as the determination period is not equal to the value 0 it. Because the fixed frequency periodic tasks executed if the current task, the cycle period will be treated as a positive number; if it is relatively fixed delay execution of the current task, it will be a negative number as a process cycle period.

Here, we see setNextRunTime method, a method called triggerTime ScheduledThreadPoolExecutor class. Next, we look at the source code triggerTime methods.

Method ScheduledThreadPoolExecutor class triggerTime

triggerTime specific method for obtaining the time delay of the next job in the queue is executed. Source as shown in FIG.

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

long triggerTime(long delay) {
	return now() +
		((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}

TriggerTime code for these two methods is relatively simple, is to get the specific time for the next mission. One thing to note is that: delay <(Long.MAX_VALUE >> 1 determines the value of delay is less than half of Long.MAX_VALUE, if less than half the value of Long.MAX_VALUE, simply return delay, or the need to handle overflow.

We see triggerTime processing method to prevent overflow of the logic used overflowFree method ScheduledThreadPoolExecutor class Next, we look at the implementation overflowFree method ScheduledThreadPoolExecutor class.

Method ScheduledThreadPoolExecutor class overflowFree

The method shown overflowFree The source code.

private long overflowFree(long delay) {
	//获取队列中的节点
	Delayed head = (Delayed) super.getQueue().peek();
	//获取的节点不为空,则进行后续处理
	if (head != null) {
		//从队列节点中获取延迟时间
		long headDelay = head.getDelay(NANOSECONDS);
		//如果从队列中获取的延迟时间小于0,并且传递的delay
		//值减去从队列节点中获取延迟时间小于0
		if (headDelay < 0 && (delay - headDelay < 0))
			//将delay的值设置为Long.MAX_VALUE + headDelay
			delay = Long.MAX_VALUE + headDelay;
	}
	//返回延迟时间
	return delay;
}

By analyzing the source overflowFree method, it can be seen that the method is essentially intended to limit overflowFree delay times of all the nodes in the queue values ​​within Long.MAX_VALUE, compareTo prevent overflow in the process.

cancel method

The main role of the cancel method to cancel the current task is executed, the source code shown below.

public boolean cancel(boolean mayInterruptIfRunning) {
	//取消任务,返回任务是否取消的标识
	boolean cancelled = super.cancel(mayInterruptIfRunning);
	//如果任务已经取消
	//并且需要将任务从延迟队列中删除
	//并且任务在延迟队列中的索引大于或者等于0
	if (cancelled && removeOnCancel && heapIndex >= 0)
		//将当前任务从延迟队列中删除
		remove(this);
	//返回是否成功取消任务的标识
	return cancelled;
}

This code is relatively simple to understand, first call the method to cancel the task, and returns whether the task has been canceled logo. If the task has been canceled, and the need to remove tasks, a task index in the queue delay is greater than or equal to 0, then the delay is removed from the current task queue. Finally Returns whether the task successfully canceled logo.

run method

The method can be said that the core run ScheduledFutureTask class method, is to achieve Runnable interface, the source code shown below.

public void run() {
	//当前任务是否是周期性任务
	boolean periodic = isPeriodic();
	//线程池当前运行状态下不能执行周期性任务
	if (!canRunInCurrentRunState(periodic))
		//取消任务的执行
		cancel(false);
	//如果不是周期性任务
	else if (!periodic)
		//则直接调用FutureTask类的run方法执行任务
		ScheduledFutureTask.super.run();
	//如果是周期性任务,则调用FutureTask类的runAndReset方法执行任务
	//如果任务执行成功
	else if (ScheduledFutureTask.super.runAndReset()) {
		//设置下次执行任务的时间
		setNextRunTime();
		//重复执行任务
		reExecutePeriodic(outerTask);
	}
}

Logic whole approach is: first determine whether the current task is a periodic task. If you can not perform periodic task under the current running state of the thread pool is canceled mission. If the current task is not periodic task, directly calls the run method FutureTask class of tasks; if the current task is a periodic task, runAndReset FutureTask class method is called to perform the task, and if the mission is successful, then the next set of tasks time, meanwhile, set a task for repeated execution.

Here, we call the run method and runAndReset method FutureTask class and method called reExecutePeriodic ScheduledThreadPoolExecutor class. Next, we look to achieve each of these methods.

The method of the run class FutureTask

FIG source FutureTask run method as classes.

public void run() {
    //状态如果不是NEW,说明任务或者已经执行过,或者已经被取消,直接返回
    //状态如果是NEW,则尝试把当前执行线程保存在runner字段中
    //如果赋值失败则直接返回
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                //执行任务
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                //任务异常
                setException(ex);
            }
            if (ran)
                //任务正常执行完毕
                set(result);
        }
    } finally {

        runner = null;
        int s = state;
        //如果任务被中断,执行中断处理
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

The overall logic of the code to: determine whether the current task state is equal NEW, if not NEW then the task or has executed, or has been canceled, direct return; if the status is NEW is then will the task execution thread referenced by unsafe class CAS is saved in the runner field, if you save fails, the direct return; tasks; task execution if an exception occurs, call setException () method to save the exception information.

Method FutureTask class runAndReset

Method source shown below.

protected boolean runAndReset() {
	if (state != NEW ||
		!UNSAFE.compareAndSwapObject(this, runnerOffset,
									 null, Thread.currentThread()))
		return false;
	boolean ran = false;
	int s = state;
	try {
		Callable<V> c = callable;
		if (c != null && s == NEW) {
			try {
				c.call(); // don't set result
				ran = true;
			} catch (Throwable ex) {
				setException(ex);
			}
		}
	} finally {
		// runner must be non-null until state is settled to
		// prevent concurrent calls to run()
		runner = null;
		// state must be re-read after nulling runner to prevent
		// leaked interrupts
		s = state;
		if (s >= INTERRUPTING)
			handlePossibleCancellationInterrupt(s);
	}
	return ran && s == NEW;
}

Method runAndReset FutureTask class logical run method is basically the same, except the method runAndReset reset execution state of the current task.

Method ScheduledThreadPoolExecutor class reExecutePeriodic

FIG reExecutePeriodic method ScheduledThreadPoolExecutor source code for the class.

void reExecutePeriodic(RunnableScheduledFuture<?> task) {
	//线程池当前状态下能够执行任务
	if (canRunInCurrentRunState(true)) {
		//将任务放入队列
		super.getQueue().add(task);
		//线程池当前状态下不能执行任务,并且成功移除任务
		if (!canRunInCurrentRunState(true) && remove(task))
			//取消任务
			task.cancel(false);
		else
			//调用ThreadPoolExecutor类的ensurePrestart()方法
			ensurePrestart();
	}
}

Overall, the logical reExecutePeriodic method is relatively simple, important to note that: when calling reExecutePeriodic method has been performed a task, and will not trigger the denial policy of the thread pool; incoming reExecutePeriodic method of task must be a periodic task .

Released 1322 original articles · won praise 2046 · Views 5.18 million +

Guess you like

Origin blog.csdn.net/l1028386804/article/details/104585295