JDK版本1.6
目录
ScheduledThreadPoolExecutor源码分析
Demo代码示例
示例了三种调度定时任务的api
public class Main{
static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 日期格式
public static void main(String[] args) throws Exception {
System.out.println("===当前时间" + df.format(new Date()));
Runnable runnable = new Runnable(){
@Override
public void run() {
System.out.println("当前时间" + df.format(new Date()));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {}
}
};
// 初始延迟1s后,开始周期性的执行任务,上一次任务执行结束和下此任务执行开始,间隔2s
//executor.scheduleWithFixedDelay(runnable, 1, 2, TimeUnit.SECONDS);
// 初始延迟1s后,每两秒执行一次任务,若任务执行时间超过间隔时间,则上次任务执行完毕立即执行下次任务
executor.scheduleAtFixedRate(runnable, 1, 2, TimeUnit.SECONDS);
// 延迟2s后执行一次任务
//executor.schedule(runnable, 2, TimeUnit.SECONDS);
System.in.read();
}
}
ScheduledThreadPoolExecutor源码分析
继承自线程池ThreadPoolExecutor,使用DelayedWorkQueue队列存放任务
而DelayedWorkQueue内部则由延时队列DelayQueue实现
DelayQueue又依赖优先级队列PriorityQueue,所以从最内部开始分析
PriorityQueue
优先级队列,使用最小堆来进行优先级排序,内部数组来存放数据,默认初始大小11,因为要使用最小堆,所以要求元素必须能够比较,要么元素实现Comparable接口,要么构造方法中传入比较器Comparator
public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {
private static final int DEFAULT_INITIAL_CAPACITY = 11;
private transient Object[] queue;
private int size = 0;
private final Comparator<? super E> comparator;
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
}
offer
每次添加元素,都根据元素当前大小,放置合适位置,始终保持数组头元素最小
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
// 扩容
if (i >= queue.length)
grow(i + 1);
size = i + 1;
// 如果不是添加第一个元素,把元素e放置在数组最后,重新向上调整堆
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
// 向上调整,判断是使用元素本身实现的Comparable,还是传入的比较器Comparator
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
// 只看一个,另一个一样的,是个元素x向上寻找位置的过程
private void siftUpComparable(int k, E x) {
// 强转类型Comparable,添加的元素没有实现Comparable报错castException
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
// 减1右移,是父节点的下标
int parent = (k - 1) >>> 1;
// 父节点对象
Object e = queue[parent];
// 本次添加的对象x比父节点大,则不用继续往上调整了,跳出循环,当前k就是元素x的位置
if (key.compareTo((E) e) >= 0)
break;
// 否则,互换位置,把父节点对象放到k位置,x暂定放到父节点位置,再次向上寻找
queue[k] = e;
k = parent;
}
queue[k] = key;
}
poll
每次取出元素,都重新调整数组,始终保持数组头元素最小
public E poll() {
if (size == 0)
return null;
int s = --size;
modCount++;
// 数组第一个即优先级最高的,最小堆堆顶最小的元素
E result = (E) queue[0];
// 取出数组最后一个元素,暂定放置到堆顶
E x = (E) queue[s];
queue[s] = null;
// 开始从堆顶向下调整,调整为最小堆
if (s != 0)
siftDown(0, x);
return result;
}
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
private void siftDownComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>)x;
// 下标过半以后,就不需要向下调整了,因为没有子节点。可以自己画个堆图看看
int half = size >>> 1; // loop while a non-leaf
while (k < half) {
// 左移加1,为两个子节点的左侧下标
int child = (k << 1) + 1; // assume left child is least
Object c = queue[child];
int right = child + 1;
// 左侧和右侧子节点比较,取较小的赋值给c
if (right < size &&
((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
c = queue[child = right];
// 和子节点中较小的那个比较,若父节点较小,则当前k为元素x的最终位置
if (key.compareTo((E) c) <= 0)
break;
// 否则交换,子节点上位,元素x暂定child位置,继续向下比较调整
queue[k] = c;
k = child;
}
queue[k] = key;
}
DelayQueue
延时队列,添加的元素都必须继承Delayed接口,接口只有一个方法getDelay(),返回元素当前剩余的时间,时间小于等于0才能被poll或take取出
take
阻塞获取到点了的元素
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// take为阻塞方法,直到获取到一个元素
for (;;) {
// 查看优先级队列堆头元素,即最小的元素
E first = q.peek();
if (first == null) {
// 不存在元素,阻塞
available.await();
} else {
// 存在元素,获取元素的延时剩余时间
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay > 0) {
// 还没到时候,阻塞一会
long tl = available.awaitNanos(delay);
} else {
// 到时间了,取出元素返回,并唤醒其他阻塞中的线程
E x = q.poll();
assert x != null;
if (q.size() != 0)
available.signalAll(); // wake up other takers
return x;
}
}
}
} finally {
lock.unlock();
}
}
poll
与take区别为 只是尝试获取,获取不到返回null
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
return null;
else {
E x = q.poll();
assert x != null;
if (q.size() != 0)
available.signalAll();
return x;
}
} finally {
lock.unlock();
}
}
offer
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// peek返回优先级队列栈顶最小元素
E first = q.peek();
// 优先级队列添加元素
q.offer(e);
// DelayQueue.take()方法为阻塞方法,队头元素没到点,线程会阻塞
// 判断原最小元素比新元素大,则表示可能马上就到点了,立即唤醒等待着的线程对该元素进行检测
if (first == null || e.compareTo(first) < 0)
available.signalAll();
return true;
} finally {
lock.unlock();
}
}
ScheduledThreadPoolExecutor
-
schedule(Runnable command, long delay, TimeUnit unit) 延迟指定的delay时长之后,只执行一次任务
- scheduleAtFixedRate(Runnable command, long initialDelay, long period,TimeUnit unit) 初始延迟1s后,每两秒执行一次任务,若任务执行时间超过间隔时间,则上次任务执行完毕立即执行下次任务
- scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) 初始延迟1s后,开始周期性的执行任务,上一次任务执行结束和下此任务执行开始,间隔2s
三个api方法代码区别就是ScheduledFutureTask对象,下面详细看源码,至于delayedExecute(t)如下,如果核心线程数没有到定义的数量,则新启动一个,延时队列加入当前ScheduledFutureTask对象
ScheduledFutureTask
new ScheduledFutureTask<Object>(command,null,triggerTime(initialDelay, unit),
unit.toNanos(period))
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}
构造方法第一个是任务
第二个是Callable的返回值,当前咱看的任务类型是Runnable,所以未null,
第三个是计划的下次触发任务时间点
第四个延迟间隔时间,0表示只执行一次schedule,大于0表示scheduleAtFixedRate,小于0表示scheduleWithFixedDelay
triggerTime()
获取下此应该触发的时间
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));
}
// 因为后续元素根据延迟时间进行加减比较,判断谁先执行
// 所以这里判断新元素和延迟队列中最小的元素相差是否超过了long最大值
// 如果超过,调整新元素的延迟时间
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
long headDelay = head.getDelay(TimeUnit.NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
实现的Delayed接口方法,返回距离触发时间还剩多少ns
compareTo(),判断优先级,即判断触发时间的远近
run()
判断周期执行或者只执行一次
public void run() {
// 是否是周期任务,即判断构造方法第四个参数是否是0,不是0就是周期任务
if (isPeriodic())
runPeriodic();
else
// 非周期任务,执行一次了事
ScheduledFutureTask.super.run();
}
// 周期任务,先执行一次,再重置触发时间,再次添加到延迟队列中,等待worker线程再次执行
private void runPeriodic() {
boolean ok = ScheduledFutureTask.super.runAndReset();
boolean down = isShutdown();
// Reschedule if not cancelled and not shutdown or policy allows
if (ok && (!down ||
(getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
!isStopped()))) {
long p = period;
if (p > 0)
// 从上次的计划触发时间往后推迟p,所以为固定频率,如果执行时间超过了延迟时间,会立即执行,因为会排到堆头,且时间已经到位
time += p;
else
// 从当前往后推迟,即本次结束到下此开始间隔p
time = triggerTime(-p);
// 添加队列,等下次执行
ScheduledThreadPoolExecutor.super.getQueue().add(this);
}
// This might have been the final executed delayed
// task. Wake up threads to check.
else if (down)
interruptIdleWorkers();
}