java.util.Timer全面解析

java.util.Timer:线程的工具,用于在后台线程中安排将来执行的任务。可以安排一次性执行任务,或定期重复执行任务。任务应该是能快速完成的。这个类是线程安全的:多线程可以共享一个Timer对象,而不需要外部同步。

这个类可以扩展到大量同时计划的任务(数千个都没有问题)。在内部,它使用二进制堆来表示其任务队列,因此调度任务的成本为O(log n),其中n是并发计划任务的数量。

下面基于JDK1.8源码全面分析Timer的实现。

来看一下Timer的属性:

TaskQueue queue = new TaskQueue();//任务队列

TimerThread thread = new TimerThread(queue);//持有任务队列的Thread。

先简单看一下TaskQueue和TimerThread的定义。

TaskQueue:表示一个计时器任务队列,一个TimerTasks的优先级队列,通过nextExecutionTime字段排序,内部使用堆实现。

属性:

TimerTask[]queue = new TimerTask[128];//采用堆排序的数组

int size = 0;代表优先队列中任务数;

TimerThread:继承自Thread。相当于一个“助手类”,实现了定时器的任务执行线程,该线程功能包括等待定时器队列上的任务,在它们触发时执行它们,重新安排重复任务,并删除取消的任务,并从队列中去除非重复任务。

属性:

下面看一下Timer的构造函数:

构造一个Timer的对象,会触发thread,那么我们看一下TimerThread干了些什么。

主要逻辑:

1、在queue上加同步器锁,当queue为空且还有线程持有timer对象,Thread wait。直到得到notify通知,这个notify是当有元素进入queue或者newTasksMayBeScheduled被置为false时会触发。

2、如果queue为空,说明没有需要处理的任务,跳出循环。

3、从queue取最近将要触发的任务,如果这个任务是CANCELLED状态,把它从queue中移除,并continue;否则,如果任务没有达到触发时间,wait(触发间隔时间)。当任务达到触发时间,如果这个任务不是重复跑的,把它从queue中移除,并把这个任务的状态置为EXECUTED。如果它是需要重复执行的,会设置它的下一次触发时间。然后执行任务的run方法执行实际的逻辑。执行完了之后,因为是一个while的无限循环,又会开始上述逻辑判断。

在上述第3条中,其实我们是依赖queue维护了一个最近触发时间的小根堆,我们看下queue的几个核心方法:

任务加入队列:

先做容量判断:数组将满时扩大为原来两倍;否则任务加入队列,注意第一个任务是放在queue[1]的位置;然后做fixUp(size);

这个方法实际是在构建一个小根堆,传入的k即为当前队列任务数量。且任务从数组下标1位置开始存放。当队列容量大于1时,先取size/2对应的元素与size对应的元素做比较,如果queue[size/2]的下一次触发时间>queue[size]的下一次触发时间,则把queue[size/2]与queue[size]对应的任务做交换。循环直到数组下标为1的元素。这样就把最近触发的任务放到了数组1的位置,最后触发的任务放到了数组末尾。

获取当前队列最近将要执行的任务

移除当前队列第一个任务

将队列最后一个任务赋给queue[1],最后一个位置设为null,任务总数减一;关键我们看一下fixDown(1)做了什么。

因为队首任务被置为了队尾的任务,则它肯定破坏了最小堆,这个方法就是要重新建立小根堆:

1、取2k对应的节点,因为k的孩子节点是2k和2k+1,

2、先判断2k和2k+1对应的值,取他们中小的,设置j的值

3、比较queue[k]与queue[j],如果执行时间不是小于关系,交换这两个任务,让下次执行时间越大的元素下移

4、循环直到队列最后位置,这样的话,执行时间离现在最久的元素被放到了队列末尾;

重复执行的任务重新设置其下次执行时间:

更新当前队首的任务的下次执行执行,同时重新调整小根堆。


目前我们还只是看了Timer的构造函数,下面我们来看Timer设置任务调度的几个方法:

schedule(TimerTasktask, long delay);

schedule(TimerTasktask, Date time);

schedule(TimerTasktask, long delay, long period);

schedule(TimerTask task, Date firstTime, long period);

以上四个方法分别表示触发一次和按频率触发多次;

scheduleAtFixedRate(TimerTasktask, long delay, long period);

scheduleAtFixedRate(TimerTasktask, Date firstTime, long period)

按固定的频率触发多次;

那么这两类方法都有按频率重复执行的逻辑,他们有什么差别呢?

我们看一下代码:

一个传的是正数一个传的是负数。而在TimerThread的mainLoop方法中,

可以看到如果period<0,下次调度时间是当前时间+间隔时间(减一个负数就是加一个正数),否则下次调度时间是下次执行时间+间隔时间,就是说他们下次触发的时间维度不一样。

以上两类方法最终都会调用

设置task的属性,将其加入到queue中,并且如果这个task是队列的第一个任务,notify阻塞在queue上面的thread。整个入队的过程持有queue的监视器锁。

下面我们看一下TimerTask,它实现了Runnable接口,其run方法由子类去实现,它有状态字段state表示任务的执行状态,包括:

默认状态是VIRGIN。

取消一个任务则调用其cancel方法

只是简单把任务状态置为CANCELLED。


总结:

Timer通过TimerThread循环触发将要执行的任务,通过TaskQueue维护一个待执行任务列表,TaskQueue内部通过一个小根堆实现待执行任务的排序。拨开底层,我们对Timer这个调度器能有一个更深的理解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容