Java线程池ScheduledThreadPoolExecutor分析1

简介

自JDK1.5开始,JDK提供了ScheduledThreadPoolExecutor类来支持周期性任务的调度。在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成。但Timer有不少的缺陷:

  • Timer是单线程模式;
  • 如果在执行任务期间某个TimerTask耗时较久,那么就会影响其它任务的调度;
  • Timer的任务调度是基于绝对时间的,对系统时间敏感;
  • Timer不会捕获执行TimerTask时所抛出的异常,由于Timer是单线程,所以一旦出现异常,则线程就会终止,其他任务也得不到执行。

ScheduledThreadPoolExecutor继承ThreadPoolExecutor来重用线程池的功能,它的实现方式如下:

  • 将任务封装成ScheduledFutureTask对象,ScheduledFutureTask基于相对时间,不受系统时间的改变所影响;
  • ScheduledFutureTask实现了java.lang.Comparable接口和java.util.concurrent.Delayed接口,所以有两个重要的方法:compareTo和getDelay。compareTo方法用于比较任务之间的优先级关系,如果距离下次执行的时间间隔较短,则优先级高;getDelay方法用于返回距离下次任务执行时间的时间间隔;
  • ScheduledThreadPoolExecutor定义了一个DelayedWorkQueue,它是一个有序队列,会通过每个任务按照距离下次执行时间间隔的大小来排序;
  • ScheduledFutureTask继承自FutureTask,可以通过返回Future对象来获取执行的结果。

类图

从ScheduledThreadPoolExecutor类声明可以看出:

ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,并且实现了接口ScheduledExecutorService;

Demo

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.fly.learn.executor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 线程池test
* @author: peijiepang
* @date 2018/11/23
* @Description:
*/
public class ThreadPoolExecutorTest {
private final static Logger LOGGER = LoggerFactory.getLogger(ThreadPoolExecutorTest.class);
//private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
//private static Executor executor = Executors.newFixedThreadPool(5);
//private static Executor executor = Executors.newSingleThreadExecutor();
//private static Executor executor = Executors.newCachedThreadPool();
private static ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);

private AtomicInteger i = new AtomicInteger(0);

public void executeTask(){
Task1 task1 = new Task1();//构建任务
// executor.execute(task1);//执行任务
executor.scheduleAtFixedRate(task1,0,1,TimeUnit.SECONDS);
}

/*
* 基本任务
*/
class Task1 implements Runnable{
public void run() {
//具体任务的业务
LOGGER.info("{}...",i.incrementAndGet());
}
}

public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutorTest test = new ThreadPoolExecutorTest();
test.executeTask();
}

}

源码解析

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}

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

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

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

从构造方法可以看出,ScheduledThreadPoolExecutor使用DelayQueue来作为线程池的工作队列,由于DelayQueue是无界队列,根据线程池的工作原理,核心参数maximumPoolSize在ScheduledThreadPoolExecutor中是没有什么意义的。
总的来说,ScheduledThreadPoolExecutor为了实现周期性执行任务,对ThreadPoolExecutor做了以下改动:

  • 工作队列使用DelayQueue;
  • 任务提交之后统统都进工作队列;
  • 获取任务的方式改变,执行了任务之后,也增加了额外的处理,具体的改变后文会一一给出详细的分析。

任务提交与调度

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}

/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
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;
}

/**
* 提交延时任务,下一次执行时间相当于是上一次的执行时间加上period,它是采用已固定的频率来执行任务
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
//构造ScheduledFutureTask
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
//提供可被子类重载或者修改的方法
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
//真正提交任务
delayedExecute(t);
return t;
}

/**
* 与scheduleAtFixedRate方法不同的是,下一次执行时间是上一次任务执行完的系统时间加上period,
* 因而具体执行时间不是固定的,但周期是固定的,是采用相对固定的延迟来执行任务
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}

ScheduledFutureTask

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// 任务被添加到ScheduledThreadPoolExecutor中的序号
/** Sequence number to break ties FIFO */
private final long sequenceNumber;

// 任务将要被执行的具体时间
/** The time the task is enabled to execute in nanoTime units */
private long time;

/**
* 任务执行的间隔周期
* Period in nanoseconds for repeating tasks. A positive
* value indicates fixed-rate execution. A negative value
* indicates fixed-delay execution. A value of 0 indicates a
* non-repeating task.
*/
private final long period;

/**
* Creates a one-shot action with given nanoTime-based trigger time.
*/
ScheduledFutureTask(Runnable r, V result, long ns) {
super(r, result);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}

/**
* Creates a periodic action with given nano time and period.
*/
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}

/**
* Creates a one-shot action with given nanoTime-based trigger time.
*/
ScheduledFutureTask(Callable<V> callable, long ns) {
super(callable);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}

/**
* ScheduledThreadPoolExecutor会把待执行的任务放到工作队列DelayQueue中,
* DelayQueue封装了一个PriorityQueue,PriorityQueue会对队列中的ScheduledFutureTask进行排序
* @param other
* @return
*/
public int compareTo(Delayed other) {
if (other == this) // compare zero if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
long diff = time - x.time;
//首先按照time排序,time小的排在前面,time大的排在后面;
//如果time相同,按照sequenceNumber排序,sequenceNumber小的排在前面,
//sequenceNumber大的排在后面,换句话说,如果两个task的执行时间相同,优先执行先提交的task。
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;
}

public long getDelay(TimeUnit unit) {
//执行时间减去当前系统时间
return unit.convert(time - now(), NANOSECONDS);
}

/**
* 任务执行逻辑
* Overrides FutureTask version so as to reset/requeue if periodic.
*/
public void run() {
// 是否是周期性任务
boolean periodic = isPeriodic();
// 当前线程池运行状态下如果不可以执行任务,取消该任务
if (!canRunInCurrentRunState(periodic))
cancel(false);
// 如果不是周期性任务,调用FutureTask中的run方法执行
else if (!periodic)
ScheduledFutureTask.super.run();
// 如果是周期性任务,调用FutureTask中的runAndReset方法执行
// runAndReset方法不会设置执行结果,所以可以重复执行任务
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}

/**
* 设置下次执行时间
* Sets the next time to run for a periodic task.
*/
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}

/**
* 取消任务
* @param mayInterruptIfRunning
* @return
*/
public boolean cancel(boolean mayInterruptIfRunning) {
boolean cancelled = super.cancel(mayInterruptIfRunning);
if (cancelled && removeOnCancel && heapIndex >= 0)
remove(this);
return cancelled;
}

/**
* 周期性任务,重新添加任务
* Requeues a periodic task unless current run state precludes it.
* Same idea as delayedExecute except drops task rather than rejecting.
*
* @param task the task
*/
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) {
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
//添加工作work线程
ensurePrestart();
}
}

/**
* ThreadPoolExecutor中的钩子方法
* Cancels and clears the queue of all tasks that should not be run
* due to shutdown policy. Invoked within super.shutdown.
*/
@Override void onShutdown() {
BlockingQueue<Runnable> q = super.getQueue();
// 获取在线程池已 shutdown 的情况下是否继续执行现有延迟任务
boolean keepDelayed =
getExecuteExistingDelayedTasksAfterShutdownPolicy();
// 获取在线程池已 shutdown 的情况下是否继续执行现有定期任务
boolean keepPeriodic =
getContinueExistingPeriodicTasksAfterShutdownPolicy();
// 如果在线程池已 shutdown 的情况下不继续执行延迟任务和定期任务
// 则依次取消任务,否则则根据取消状态来判断
if (!keepDelayed && !keepPeriodic) {
for (Object e : q.toArray())
if (e instanceof RunnableScheduledFuture<?>)
((RunnableScheduledFuture<?>) e).cancel(false);
q.clear();
}
else {
// Traverse snapshot to avoid iterator exceptions
for (Object e : q.toArray()) {
if (e instanceof RunnableScheduledFuture) {
RunnableScheduledFuture<?> t =
(RunnableScheduledFuture<?>)e;
// 如果有在 shutdown 后不继续的延迟任务或周期任务,则从队列中删除并取消任务
if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||
t.isCancelled()) { // also remove if already cancelled
if (q.remove(t))
t.cancel(false);
}
}
}
}
tryTerminate();
}

delayedExecute

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
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
// 确保线程池中至少有一个线程启动,即使corePoolSize为0
// 该方法在ThreadPoolExecutor中实现
ensurePrestart();
}
}

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

总结

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,实现了ScheduledExecutorService接口,该接口定义了schedule等任务调度的方法。
同时ScheduledThreadPoolExecutor有两个重要的内部类:DelayedWorkQueue和ScheduledFutureTask。ScheduledFutureTask继承自FutureTask,并且实现了Delayed接口,DelayedWorkQueue将会在接下来的篇幅分析,敬请期待。