ScheduledThreadPoolExecutor的scheduleAtFixedRate方法探究
ScheduledThreadPoolExecutor除了具有ThreadPoolExecutor的所有功能外,还可以延迟执行任务或者周期性的执 行某个任务。scheduleWithFixedDelay和scheduleAtFixedRate就是用来完成这个功能的。平常使用 scheduleAtFixedRate这个方法时并没有多想,但是这几天在实现一个功能的时候,需要考虑scheduleAtFixedRate所执行 的task是否会影响任务的周期性,比如scheduleAtFixedRate(command,5,10,TimeUnit.SECONDS),那么 这个command的执行会不会影响这个10秒的周期性。因此特意仔细看了下ScheduledThreadPoolExecutor的源代码,这里记录 一下,以便以后查看。
scheduleAtFixedRate有两个时间参数,initialDelay和period,对应该方法的两个主要功能,即延迟运行任务和周期性执行任务。
- 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();
- RunnableScheduledFuture<?> t = decorateTask(command,
- new ScheduledFutureTask<Object>(command,
- null,
- triggerTime(initialDelay, unit),
- unit.toNanos(period)));
- delayedExecute(t);
- return t;
- }
- /**
- * Specialized variant of ThreadPoolExecutor.execute for delayed tasks.
- */
- private void delayedExecute(Runnable command) {
- if (isShutdown()) {
- reject(command);
- return;
- }
- // Prestart a thread if necessary. We cannot prestart it
- // running the task because the task (probably) shouldn't be
- // run yet, so thread will just idle until delay elapses.
- if (getPoolSize() < getCorePoolSize())
- prestartCoreThread();
- super.getQueue().add(command);
- }
首先创建一个ScheduledFutureTask,然后通过delayedExecute执行这个task。在delayedExecute中,首先 预先启动一个线程,这里要注意的是这个这里用来启动一个新线程的firstTask参数是null,所以新启动的线程是idle状态的,然后把这个 task加入到workQueue。ScheduledThreadPoolExecutor里的workQueue用的是 DelayedWorkQueue,这个DelayedWorkQueue就是实现delay的关键。DelayedWorkQueue内部使用的是 DelayQueue,DelayQueue实现task delay的关键就在于其Offer(E e)和Take.下面,通过分析这两个方法和结合ThreadPoolExecutor的运行原理来说明delay操作是如何实现的
- public boolean offer(E e) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- E first = q.peek();
- q.offer(e);
- if (first == null || e.compareTo(first) < 0)
- available.signalAll();
- return true;
- } finally {
- lock.unlock();
- }
- }
- 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(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();
- }
- }
ScheduledThreadPoolExecutor执行task是通过工作线程Work来承担的,Work的Run方法如下:
- public void run() {
- try {
- Runnable task = firstTask;
- firstTask = null;
- while (task != null || (task = getTask()) != null) {
- runTask(task);
- task = null;
- }
- } finally {
- workerDone(this);
- }
- }
因为前面在delayedExecute方法里面创建work线程的firstTask参数为null,所以就通过getTask去从workQueue 里面获取task,getTask在正常情况下(即线程池没有关闭,线程数量没有超过corePoolSize等)是通过 workQueue.take()从workQueue里获取任务。根据上面的贴出来的take方法的代码,如果queue是空的,则take方法会阻塞 住,直到有新task被add进来。而在上面的delayedExecute方法的最后,会把创建的scheduledFutureTask加入到 workQueue,这样take方法中的available.await()就被唤醒;在take方法里面,如果workQueue不为空,则执行 task.getDelay()方法获取task的delay
- public long getDelay(TimeUnit unit) {
- return unit.convert(time - now(), TimeUnit.NANOSECONDS);
- }
这里的time是通过两个方法把initialDelay变成一个triggerTime
- /**
- * Returns the trigger time of a delayed action.
- */
- private long triggerTime(long delay, TimeUnit unit) {
- return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
- }
- /**
- * Returns the trigger time of a delayed action.
- */
- long triggerTime(long delay) {
- return now() +
- ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
- }
注意看这个方法,这里返回的delay不是固定不变的,从task被放入workQueue起,不同的时间调用getDelay方法会得出不同的 delay。如果放入workQueue的task的initialDelay是5秒,那么根据take方法的代码,如果在放入workQueue5秒 后,就可以从delayQueue中拿到5秒前put进去的task,这样就实现了delay的功能。
在本文的最前面提到scheduleAtFixedRate能够周期性地执行一项任务,那么这个是如何实现的呢?在 scheduleAtFixedRate方法里创建了一个ScheduledFutureTask,这个ScheduledFutureTask包装了 command,最后周期性执行的是ScheduledFutureTask的run方法。
- 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)
- time += p;
- else
- 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();
- }
- /**
- * Overrides FutureTask version so as to reset/requeue if periodic.
- */
- public void run() {
- if (isPeriodic())
- runPeriodic();
- else
- ScheduledFutureTask.super.run();
- }
由上面的代码可以看出,scheduleAtFixedRate(command,5,10,TimeUnit.SECONDS)这个方法的周期性会受 command的影响,如果command方法的执行时间是10秒,那么执行command的周期其实是20秒,即 scheduleAtFixedRate这个方法要等一个完整的command方法执行完成后才继续周期性地执行command方法,其实这样的设计也是 符合常理的。
以上就是对ScheduledThreadPoolExecutor的一点小理解。
来自:http://olylakers.iteye.com/blog/1218243
ScheduledThreadPoolExecutor的scheduleAtFixedRate方法探究的更多相关文章
- ScheduledExecutorService中scheduleAtFixedRate方法与scheduleWithFixedDelay方法的区别
ScheduledExecutorService中scheduleAtFixedRate方法与scheduleWithFixedDelay方法的区别 ScheduledThreadPoolExecut ...
- Android View各种尺寸位置相关的方法探究
Android View各种尺寸位置相关的方法探究 本来想做一个View间的碰撞检测之类的. 动手做了才发现不是想象的那么简单. 首先,写好了碰撞检测的工具类如下: package com.mengd ...
- 简单理解java中timer的schedule和scheduleAtFixedRate方法的区别
timer的schedule和scheduleAtFixedRate方法一般情况下是没什么区别的,只在某个情况出现时会有区别--当前任务没有来得及完成下次任务又交到手上. 我们来举个例子: 暑假到了老 ...
- pandas重置索引的几种方法探究
pandas重置索引的几种方法探究 reset_index() reindex() set_index() 函数名字看起来非常有趣吧! 不仅如此. 需要探究. http://nbviewer.jupy ...
- 关于scheduleAtFixedRate方法与scheduleWithFixedDelay的使用
一.scheduleAtFixedRate方法 该方法是ScheduledExecutorService中的方法,用来实现周期性执行给定的任务,public ScheduledFuture<?& ...
- Timer的schedule和scheduleAtFixedRate方法的区别解析(转)
在java中,Timer类主要用于定时性.周期性任务 的触发,这个类中有两个方法比较难理解,那就是schedule和scheduleAtFixedRate方法,在这里就用实例分析一下 (1)sched ...
- Timer的schedule和scheduleAtFixedRate方法的区别解析
在java中,Timer类主要用于定时性.周期性任务 的触发,这个类中有两个方法比较难理解,那就是schedule和scheduleAtFixedRate方法,在这里就用实例分析一下 (1)sched ...
- ScheduledThreadPoolExecutor之remove方法
之前用定时任务的线程池,设置了个任务,但是突然今天产品说,某些个操作需要中断某些任务(如果任务还没有执行),使其不能再到点执行了.于是查了API果然有这样一个方法. 一看API,需要移除的是一个Run ...
- java设计模式之工厂方法探究
简单工厂 + 工厂方法 + 抽象工厂 看了十几篇博客,每篇基本上都能有个自己的解释,我汇总这些内容,重新梳理整理了一番,以形成自己的理解. 简单工厂模式其实不算23种设计模式之 ...
随机推荐
- 51nod2000 四边形分割平面 规律题
观察样例,$ans(1) = 1, ans(2) = 10$,再手推一组,$ans(3) = 26$ 可以发现规律$ans(n) = (2n - 1)^2 + 1$ 如果还是没看出规律,那么打个程序去 ...
- bzoj 5294: [Bjoi2018]二进制
Description pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是333 的倍数.他想研究对于二进 制,是否也有类似的性质.于是他生成了一个长为n 的二进制串,希望 ...
- 2017-2018-1 JAVA实验站 冲刺 day06
2017-2018-1 JAVA实验站 冲刺 day06 各个成员今日完成的任务 小组成员 今日工作 完成进度 张韵琪 进行工作总结.博客.小组成员头像 100% 齐力锋 找背按钮声音 100% 张浩 ...
- [HDU5965]扫雷
[HDU5965]扫雷 题目大意: 一个\(3\times n(n\le10000)\)的扫雷,第\(2\)排没有雷.告诉你第\(2\)排上的数,问有几种埋雷的方案? 思路: 动态规划. 将\(1,3 ...
- 斐波那契数列(Java实现)
描述 一个斐波那契序列,F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2) (n>=2),根据n的值,计算斐波那契数F(n),其中0≤n≤1000. 输入 输入 ...
- 如何解决IIS7上传文件大小限制,.NET 上传文件后 找不到目录解决
IIS7 默认文件上传大小是30M,那么超过30M的文件就无法上传了,那么就需要对IIS的配置文件进行修改. 在实际应用中往往会出现上传文件太大,无法上传的情况,那是因为IIS对上传文件大小有限制,I ...
- Java性能优化的9大工具
在这篇文章中,我会带着大家一起看一下9个可以帮助我们优化Java性能的工具.有一些我们已经在IDR Solutions中使用了,而另外一些有可能在个人项目中使用. NetBeans Profiler ...
- [Win32]创建模态窗口
http://www.cnblogs.com/zplutor/archive/2011/02/20/1958973.html 在Win32编程中,如果要显示一个模态窗口,一般是先创建对话框模板,然后使 ...
- VS2010 + IDA SDK 搭建IDA Plugin开发环境
http://www.h4ck.org.cn/2011/11/vs2010-idasdk6-2-ida-plugin-development/ 1. 执行菜单的File->New->Pro ...
- ubuntn 内核升级到LINUX v4.11.8:
升级到LINUX v4.11.8: http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11.8/linux-headers-4.11.8-041108_ ...