)
Posted ΘLLΘ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了)相关的知识,希望对你有一定的参考价值。
文章目录
《Java并发编程的艺术》读后笔记-ScheduledThreadPoolExecutor详解(第十章)
1.ScheduledThreadPoolExecutor简介
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。
ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数
2.ScheduledThreadPoolExecutor的运行机制
ScheduledThreadPoolExecutor
的执行示意图:
DelayQueue
是一个无界队列,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中没有什么意义(设置maximumPoolSize的大小没有什么效果)
ScheduledThreadPoolExecutor
的执行主要分为两大部分:
- 当调用ScheduledThreadPoolExecutor的
scheduleAtFixedRate()
方法或者scheduleWithFixedDelay()
方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur
接口的ScheduledFutureTask
。 - 线程池中的线程从DelayQueue中获取
ScheduledFutureTask
,然后执行任务。
ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改:
- 使用DelayQueue作为任务队列。
- 获取任务的方式不同
- 执行周期任务后,增加了额外的处理
3.ScheduledThreadPoolExecutor的实现
前面我们提到过,ScheduledThreadPoolExecutor会把待调度的任务ScheduledFutureTask放到一个DelayQueue中。
ScheduledFutureTask
主要包含3个成员变量:
- long型成员变量
time
,表示这个任务将要被执行的具体时间。 - long型成员变量
sequenceNumber
,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号。 - long型成员变量
period
,表示任务执行的间隔周期。
DelayQueue封装了一个PriorityQueue
,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序:
- 排序时,
time
小的排在前面(时间早的任务将被先执行)。 - 如果两个ScheduledFutureTask的time相同,就比较
sequenceNumber
,sequenceNumber小的排在前面(也就是说,如果两个任务的执行时间相同,那么先提交的任务将被先执行)。
ScheduledThreadPoolExecutor
中的线程执行周期任务的过程:
这四个步骤具体说明:
- 线程1从DelayQueue中获取已到期的ScheduledFutureTask(
DelayQueue.take()
)。到期任务是指ScheduledFutureTask的time大于等于当前时间。 - 线程1执行这个ScheduledFutureTask。
- 线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。
- 线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(
DelayQueue.add()
)。
DelayQueue.take()
方法的源代码实现:
public E take() throws InterruptedException
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try
for (;;)
E first = q.peek();
if (first == null)
available.await();
else
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
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 && q.peek() != null)
available.signal();
lock.unlock();
DelayQueue.take()
的执行示意图:
如图所示,获取任务分为3大步骤:
- 获取Lock
- 获取周期任务
- 如果PriorityQueue为空,当前线程到Condition中等待
- 如果PriorityQueue的头元素的time时间比当前时间大,到Condition中等待到time时间
- 获取PriorityQueue的头元素;如果PriorityQueue不为空,则唤醒在Condition中等待的所有线程
- 释放Lock
ScheduledThreadPoolExecutor在一个循环中执行步骤2,直到线程从PriorityQueue获取到一个元素之后(执行2.3.1之后),才会退出无限循环(结束步骤2)。
DelayQueue.add()
的源代码实现:
public boolean offer(E e)
final ReentrantLock lock = this.lock;
lock.lock();
try
q.offer(e);
if (q.peek() == e)
leader = null;
available.signal();
return true;
finally
lock.unlock();
DelayQueue.add()
的执行示意图:
如图所示,添加任务分为3大步骤
- 获取Lock
- 添加任务:
- 向PriorityQueue添加任务。
- 如果在上面2.1中添加的任务是PriorityQueue的头元素,唤醒在Condition中等待的所有线程。
- 释放Lock
以上是关于)的主要内容,如果未能解决你的问题,请参考以下文章