惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
Blog — PlanetScale
Blog — PlanetScale
V
Visual Studio Blog
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
博客园 - Franky
IT之家
IT之家
博客园 - 叶小钗
Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
雷峰网
雷峰网
腾讯CDC
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
人人都是产品经理
人人都是产品经理
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
博客园_首页
G
Google Developers Blog

祈雨的笔记

安全多方计算MPC spark原理解析 kueue执行源码分析 spark on k8s执行源码分析 spark-operator源码解析 系统压测遇到的缓存击穿问题 我的世界PC与安卓联机 蚂蚁金服流量投放平台的AIG改造 G1大对象致Old区占用率高 日志打印导致接口响应率下跌分析 Groovy加载类导致OOM分析 ERROR日志打印导致CPU满载 记OceanBase死锁超时 应用发版期间服务响应超时 Ark Serverless初探 系统优化复盘一二三 The user specified as a definer does not exist Kong网关初探 API网关选型调研 CPU火焰图常用工具 配置中心选型调研 root操作Nginx导致用户组错误 基于Proxifier使用代理 FastJSON字段智能匹配踩坑 Nacos初探 记一次Nginx服务器CPU满荷载故障 基于券系统分库分表的思考 limit不参与SQL成本计算致索引失效 Linux常用性能监控命令 golang低版本http2偶现400
定时任务之ScheduledThreadPoolExecutor
祈雨的笔记 · 2019-05-22 · via 祈雨的笔记

java自带的定时任务执行器为ScheduledThreadPoolExecutor,继承于线程池管理器ThreadPoolExecutor,常用方法如下,程序将每2秒输出一次系统时间。

1
2
3
4
5
6
7
8
9
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(DateFormatUtils.format(System.currentTimeMillis(), "HH:mm:ss SSS"));
}
}, 0, 2, TimeUnit.SECONDS);
Thread.sleep(10_000);
executor.shutdown();

java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate方法把参数Runnable对象封装成自定义的定时任务java.util.concurrent.ScheduledThreadPoolExecutor.ScheduledFutureTask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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<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;
}

ScheduledThreadPoolExecutor的阻塞队列为其自实现的DelayedWorkQueue,实际上是一个按定时任务RunnableScheduledFuture的下次执行时间排序的有序队列。

ScheduledThreadPoolExecutor中每次添加一个定时任务时,调用方法java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue#offer(java.lang.Runnable)DelayedWorkQueue添加元素。DelayedWorkQueue的具体实现如下,如果队列为空,则元素直接置入为队列首位;如果队列不为空,则队列以任务的下一次执行时间顺序排序。

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
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();

RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = size;

if (i >= queue.length)
grow();
size = i + 1;
if (i == 0) {

queue[0] = e;
setIndex(e, 0);
} else {

siftUp(i, e);
}
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}

private void siftUp(int k, RunnableScheduledFuture<?> key) {
while (k > 0) {
int parent = (k - 1) >>> 1;
RunnableScheduledFuture<?> e = queue[parent];
if (key.compareTo(e) >= 0)
break;
queue[k] = e;
setIndex(e, k);
k = parent;
}
queue[k] = key;
setIndex(key, k);
}

ScheduledThreadPoolExecutor添加Worker创建线程调用的父类的方法java.util.concurrent.ThreadPoolExecutor#addWorker,然后新建的线程程调用方法java.util.concurrent.ThreadPoolExecutor#runWorker读取阻塞队列获取待执行的任务。

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
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {

while (task != null || (task = getTask()) != null) {
w.lock();




if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {

processWorkerExit(w, completedAbruptly);
}
}

这块代码的关键代码为while (task != null || (task = getTask()) != null),作用为读取阻塞队列中的待执行任务。getTask()中Worker线程从阻塞获取元素,而ScheduledThreadPoolExecutorDelayedWorkQueue在获取元素时,如果有任务的下一次执行时间到了则立即返回,否则通过互斥锁阻塞直到有新元素添加或者有元素的下一次执行时间到了。

因此Worker线程在while循环中不断阻塞-执行任务-阻塞,从而实现了循环间隔固定时间执行任务的效果。