Java的Timer原理

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://comdean.blogbus.com/logs/4697030.html

一提到定时器(不是时钟)如何实现,第一想法就是汇编,x86平台相关内容找本微机原理教材就能翻到。如果要在Java上实现呢?Java既然是运行在JVM上的,那么虚拟机提供的指令集或许能有办法。中断处理?这是我的第一想法。
但是看到Timer类的代码之后,发现Josh Bloch跟我的想法大不一样,他用的是Java本身的API实现的,就是一个普普通通的类而已,完全是高来高去,根本没有涉及到任何神秘的底层指令。
整个机理就是建立在Java的多线程技术基础之上的。
比如我们用闹钟每天早上定时起床吧。我们这里考虑的只是理想状况,我们除了闹钟振铃不会听到闹钟的任何声音,因为有人听见机械表的走时会睡不着;闹钟除了我们操作它,否则也不会受到我们任何影响,不用考虑放到床头夜里被我们踢到床下。
这种前提下,我们睡觉的时候,闹钟严格按照自己的方式走时,即使它自己坏掉了也跟我们睡觉没有任何关系。而我们睡觉也不会因为闹钟走时而有什么影响。早上到了指定时间,闹钟就会振铃唤醒我们。
Java的定时器就是这个原理。Java语言,根类Object就提供了wait(long timeout)方法,这个方法会让一个线程阻塞指定的timeout时间,看到这里我们好像有点思路了。我们指定一个线程A,调用对象B.wait(timeout),线程A就会阻塞,直到timeout到了,B醒来会使A继续执行。事实上的原理也就是这样的,具体的Timer实现要更加复杂一些。
看到这里可能有点疑问:
1.没看到多线程啊,只有A是个Thread,B可是个对象啊。
2.Thread类里面不是有一个sleep()方法吗?能精确到毫秒,为什么不用这个?
第一个问题比较简单,其实Timer类是为多任务定时设计的,在实现里面,B是一个任务队列(实现上就是一个array),维护着所有使用当前Timer定时的任务,它们可是一堆货真价实的线程实例。每次线程A都取队列中距离当前时间最近的的定时任务,跟当前时间比较,然后wait(timeout)这段时间。线程唤醒的时刻也是队列中这个定时任务运行的时刻。然后线程继续取下一个定时任务,继续wait(timeout)。从这里我们能看出来,每次定时都有额外的时间开销,比如要维护队列等,所以Java的Timer类不保证实时。
第二个问题,其实也很简单,Thread类本身的sleep(timeout)方法调用之后,线程并不是真正睡眠了,依然会被调度器调度,只有使用interrupt() 方法才能中断sleep()方法,这样看来,sleep()方法并不能算是真正意义的sleep;而wait方法会使得线程真正睡眠,直至被唤醒、中断或者参数时间耗尽。
猛一看Java,wait()好像是一个让人有点奇怪的地方,wait()方法是在根类Object里面实现的,也就说所有的Java类都会因此而继承wait()方法,而不仅仅在线程类里面。但wait()方法被定义为普世通用的,却仅仅在多线程里用的到。原因也很简单,临界区锁是不应该指定对象类型的,使用锁的wait()方法来阻塞整个线程,在其他地方按照逻辑唤醒锁,进而唤醒整个线程,是一个合理而正确的选择。
这里大致说说实现的原理,想画画具体的执行流程的,但是多线程的执行实在不好描述,看源代码是最好的办法。

你可能感兴趣的:(java)