【JUC源码解析】ScheduledThreadPoolExecutor
简介
它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行。在多线程或者对灵活性有要求的环境下,要优于java.util.Timer。
提交的任务在执行之前支持取消,默认情况下,在延迟到来之前,不会自动从队列中删除,但可以设置,使其立刻从队列中移除。
有两种模式,固定频率(scheduleAtFixedRate)和固定延迟(scheduleWithFixedDelay),不管哪种模式,同一个任务不会被叠加执行,即便是不同的线程执行同一个任务。
继承ThreadPoolExecutor,维护一个固定大小的线程池和一个无界延迟队列(delay queue)。
ScheduledFutureTask,用来描述要执行的任务,DelayedWorkQueue,则是装在这些任务的delay queue.
固定频率
一个任务,从第一次开始执行的时间点开始,每隔一定的时间执行一次,如果执行的时间大于间隔时间,则要等这次执行结束,再执行下一次。

如上图所示,蓝色表示任务执行,白色表示间隔时间。
固定延迟
一个任务,每一次执行结束之后,延迟一定的时间,执行下一次。

如上图所示,蓝色表示任务执行,白色表示间隔时间。
源码分析
属性
private volatile boolean continueExistingPeriodicTasksAfterShutdown; // shut down之后,是否取消period任务
private volatile boolean executeExistingDelayedTasksAfterShutdown = true; // shut down之后,是否取消non-period任务
private volatile boolean removeOnCancel = false; // cancel后,是否从队列里移除此任务
private static final AtomicLong sequencer = new AtomicLong(); // 给任务编号
ScheduledFutureTask
属性
private final long sequenceNumber; // 序列编号
private long time; // 执行时间
private final long period; // 周期,正值:固定频率;负值:固定延迟;0:不重复执行
RunnableScheduledFuture<V> outerTask = this; // 实际任务
int heapIndex; // 堆索引
构造方法
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();
}
关键方法
getDelay(TimeUnit)
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), NANOSECONDS);
}
compareTo(Delayed other)
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;
}
setNextRunTime()
private void setNextRunTime() { // 设置下一次执行时间
long p = period;
if (p > 0) // 固定频率,从第一次时间点,每次加period
time += p;
else
time = triggerTime(-p); // 固定延迟,每次执行结束后,加period作为下一次执行时间
}
cancel(boolean mayInterruptIfRunning)
public boolean cancel(boolean mayInterruptIfRunning) { // 取消任务
boolean cancelled = super.cancel(mayInterruptIfRunning);
if (cancelled && removeOnCancel && heapIndex >= 0)
remove(this);
return cancelled;
}
run()
public void run() { // 执行任务
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run(); // 单次执行
else if (ScheduledFutureTask.super.runAndReset()) { // 周期执行,runAndReset
setNextRunTime(); // 设置下次执行时间
reExecutePeriodic(outerTask); // 重新加入队列
}
}
构造方法
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);
}
关键方法
scheduleAtFixedRate
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)); // period 大于 0
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
scheduleWithFixedDelay
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)); // -delay 小于 0
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
delayedExecute(RunnableScheduledFuture<?> task)
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown()) // 如果线程池已经shut down,则拒绝任务
reject(task);
else {
super.getQueue().add(task); // 否则,添加任务到延迟队列
if (isShutdown() && !canRunInCurrentRunState(task.isPeriodic()) && remove(task))
task.cancel(false); // 根据run-after-shutdown参数,决定是否取任务
else
ensurePrestart(); // 保证线程启动
}
}
reExecutePeriodic(RunnableScheduledFuture<?> task)
void reExecutePeriodic(RunnableScheduledFuture<?> task) { // 周期性任务重新入队,策略同delayedExecute
if (canRunInCurrentRunState(true)) {
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
ensurePrestart()
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
该方法在ThreadPoolExecutor类,保证线程池中至少有一个活动线程。
triggerTime()
long triggerTime(long delay) { // 返回延迟动作的触发时间
return now() + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
private long overflowFree(long delay) { // 处理溢出情况
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
long headDelay = head.getDelay(NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
DelayedWorkQueue
属性
private static final int INITIAL_CAPACITY = 16; // 初始容量
private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY]; // 堆,充当优先级队列
private final ReentrantLock lock = new ReentrantLock(); // 可重入锁
private int size = 0;
private Thread leader = null; // 领导者线程
private final Condition available = lock.newCondition(); // 条件队列
关键方法
以下这些方法的解释可参考前两篇文章,【JUC源码解析】DelayQueue和【JUC源码解析】PriorityBlockingQueue
siftUp
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);
}
siftDown
private void siftDown(int k, RunnableScheduledFuture<?> key) { // 向下调整
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
RunnableScheduledFuture<?> c = queue[child];
int right = child + 1;
if (right < size && c.compareTo(queue[right]) > 0)
c = queue[child = right];
if (key.compareTo(c) <= 0)
break;
queue[k] = c;
setIndex(c, k);
k = child;
}
queue[k] = key;
setIndex(key, k);
}
grow
private void grow() { // 扩容
int oldCapacity = queue.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%
if (newCapacity < 0) // overflow
newCapacity = Integer.MAX_VALUE;
queue = Arrays.copyOf(queue, newCapacity);
}
offer
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;
}
take
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
long 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 {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
行文至此结束。
尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_stpe.html
【JUC源码解析】ScheduledThreadPoolExecutor的更多相关文章
- 【JUC源码解析】SynchronousQueue
简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...
- 【JUC源码解析】ForkJoinPool
简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...
- 【JUC源码解析】DelayQueue
简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...
- 【JUC源码解析】CyclicBarrier
简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...
- 【JUC源码解析】ConcurrentLinkedQueue
简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ...
- 【JUC源码解析】Exchanger
简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...
- Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...
- Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...
- Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...
随机推荐
- Office 365实现单点登录系列(3)—使用Azure AD Connect 进行目录同步
Hello 小伙伴们,我回来了~ 2017年底中招了流感,还得了结膜炎,我也是无奈的···但使命感驱使我还是要把文章更完(这么敬业还不点赞关注(*^__^*) ) 我们接着上一篇文章继续说,上一篇已经 ...
- Mysql不改默认侦听回环地址而ssh通道连接的办法
Mysql不改默认侦听回环地址而ssh通道连接的办法 文:铁乐与猫 比较新版的mysql默认侦听的网卡地址是本地回环地址:127.0.0.1: 比如你在/etc/mysql/my.cnf配置文件中往往 ...
- 第八章 计时器(BEEPER2)
/*------------------------------------- BEEPER2.C -- Timer Demo Program No.1 (c) Charles Petzold, 19 ...
- ZT pthread_cleanup_push()/pthread_cleanup_pop()的详解
pthread_cleanup_push()/pthread_cleanup_pop()的详解 分类: Linux 2010-09-28 16:02 1271人阅读 评论(1) 收藏 举报 async ...
- MySQL(27):行锁、表锁、乐观锁、悲观锁
1. 首先说一下:行锁 和 表锁 主要是针对锁粒度划分的. 一般分为:行锁.表锁.库锁 (1)行锁:访问数据库的时候,锁定整个行数据,防止并发错误. (2)表锁:访问数据库的时候,锁定整个表数据,防 ...
- SICP 习题 (1.34)解题总结
SICP 习题 1.34的题目比較特别一点.对于没有接触过高阶函数的同学们来说是个非常好的学习机会. 题目是这种,假设我们定义以下的过程: (define (f g) (g 2)) 那么就有: ( ...
- 封装一个统一返回json结果类JsonResult
import java.io.Serializable; public class JsonResult implements Serializable{ private static final l ...
- Word中摘要和正文同时分栏后,正文跑到下一页,怎么办?或Word分栏后第一页明明有空位后面的文字却自动跳到第二页了,怎么办?
问题1:Word中摘要和正文同时分栏后,正文跑到下一页,怎么办?或Word分栏后第一页明明有空位后面的文字却自动跳到第二页了,怎么办? 答:在word2010中,菜单栏中最左侧选“文件”->“选 ...
- PHP的Reflection反射机制
更多内容推荐微信公众号,欢迎关注: 原文地址: http://www.nowamagic.net/php/php_Reflection.php PHP5添加了一项新的功能:Reflection.这个功 ...
- iPhone将NSString转换编码集为gb2312或者gbk的方法
很多时候软件读取的中文网页编码集是gb2312,所以显示出来的是乱码.这时需要将NSString文字编码转换.你可以试试以下代码 NSURL *url = [NSURL URLWithString:u ...