多线程之----定时器TIMER

时间:2021-01-30 23:18:52

结上一篇  多线程的简单介绍  http://www.cnblogs.com/duanxiaojun/p/6595847.html

在上一讲中我主要是对多线程学习这个系列做了一个大致的学习计划,然后对实现线程的两种方式做了介绍,但是在上一讲中漏说了一点就是为什么java要提供集成Thread和实现runnable接口这两种方式来实现线程,这个问题是我在面试的时候被问到的,很可惜之前从没了解过为什么-------这也给自己一个教训就是以后学什么东西要试着去问自己为什么是这样的,人家为什么这样去设计。好了针对这个问题在我查阅了很多资料后得出的是如下的解释:

首先说一下,我们知道java的集成只能是单继承,不能多集成,这样的话就会有缺陷,比如想增加一个功能的时候必须要去修改基类。而实现runnable接口的这种方式可以很好的解决java不能多继承导致的缺陷。这是第一个原因。

再说第二个,我们知道实现runnable接口的方式代码的写法是这样的:new Thread(new runnable(){ public void run(){    ....}})。在这种情况下我们可以看到其实整个的runnable对象中的代码可以被多个Thread对象实例所使用共享,这样就可以解决一个多个线程处理同一资源的情况。做到了线程安全。在这里我觉着有必要通过一个代码的方式来解释一下第二个优点是如何实现的。我们拿一个卖火车票的例子,当然也可以是其他的有唯一资源的例子都可以。下面先看代码如下和对代码的解释:

多线程之----定时器TIMER多线程之----定时器TIMER多线程之----定时器TIMER

上面第对这个优点的一个代码的展示。

好了 除了这两个外 我们说其实在真正的项目中我们使用实现接口的方式是比较多的。

说到这里好像没有说线程的一些状态,线程有如下的几种状态:创建,就绪,堵塞,终止。对这四种状态大家应该比较熟悉吧,这时候我又想起来一个面试的时候被问的,就是说说sleep和wait有什么区别,其实从字面意思也不难理解,sleep是睡眠,睡指定时候后就自己到就绪队列中等待这cpu时间片的轮询到的时候就占用cpu资源开始执行。而wait是等待,知道有人去主动的唤醒他notify或者唤醒全部notifyAll,在等待的时候是不占用资源的,也就是资源已经被释放。其他的我想应该没什么别的了。主要是看面试官想问什么了。

     好了上面是对上一篇的一个总结和补充,下面开始今天的有关javaTimer定时器的学习。

首先来说一下Timer是怎么工作的,Timer 是按照一定的时间段或者一个时间点根据定时的定时任务进行执行的。

Timer这个java提供的定时器有如下的特点:

① 他是一个单线程的,也就是你启动一个Timer定时器就是启动了一个线程。

② Timer定时器默认的情况下不是守护线程,但是可以通过构造参数设置为守护线程,守护线程在没有其他线程的情况下自己会挂掉。

③ 使用Timer定时器的时候 要跟一个TimerTask定时任务结合来使用。而且TimerTask其实底层就是一个队列,在TimerTask中增加的任务会在定时器这个线程里面挨个的执行。TimerTask也有自己的cannel取消等方法。

④ TimerTask中的run方法无法抛出,所以要进行try catch捕获,如果其中任何一个任务发生异常没有被捕获,则其他任务也将被终止

说了这些概念,我们先来一个代码例子来看看是这么执行的吧。

多线程之----定时器TIMER

我们看到TImer定时器这个类有两个schedule方法。其中都有的就是一个TimerTask这个任务。我对这两个方法进行了一个总结-----网友提供。。

方法详解:
(1)schedule(TimerTask task, Date executeTime)
当executeTime<=currentTime时,task任务会在currentTimer立即执行
当executeTime>currentTime时,task会在未来的executeTime执行
 
(2)schedule(TimerTask task, Date firstTime, long period)
当firstTime <=currentTime时,task任务会在currentTimer立即执行,
当firstTime >currentTime时,task会在未来的executeTime执行,
执行任务所用的时间taskUsedTime<peroid,则下一个任务执行的时间是上次任务执行完成的时间+peroid,任务按时间间隔peroid周期性执行任务
执行任务所用的时间taskUsedTime>peroid,则下一个任务执行的时间是上次任务执行完成的时间+taskUsedTime,任务按时间间隔taskUsedTime 周期性执行任务
 
 
(3)schedule(TimerTask task, long delay)
任务延迟delay毫秒进行执行
 
(4)schedule(TimerTask task, long delay, long period)
A、延迟delay毫秒第一次执行,
B、执行任务所用的时间taskUsedTime<peroid,则下一个任务执行的时间是上次任务执行完成的时间+peroid, 任务按时间间隔peroid周期性执行任务
C、执行任务所用的时间taskUsedTime>peroid,则下一个任务执行的时间是上次任务执行完成的时间+taskUsedTime, 任务按时间间隔taskUsedTime 周期性执行任务
 
(5)scheduleAtFixedRate(TimerTask task, long delay, long period)
 
(6)scheduleAtFixedRate(TimerTask task, Date firstTime,  long period)
         startTime = currentTime
A、当firstTime>currentTime,任务则在currentTime执行
B、当firstTime<currentTime,任务会发生追赶执行,追赶执行的次数expectCount=(currentTime-firstTime)/peroid+1;
  第一个peroid属于追赶阶段,如果追赶上则等待执行startTime+peroid时间任务,如果没有追赶上则直接执行startTime+peroid时间的任务
 
对于Timer这个定时器的使用,我想掌握到这里就差不多了。但是还是接上一个的话题,我要知道java在实现这个定时器的时候到底是怎么做的呢,哪就让我们去看看源码吧,

 首先我们看一下Timer这个类的构造函数,因为我们知道我们再使用Timer这个类的时候我们只是创建了一个Timer对象,并没有像Thread那样主动的去调用start方法。所以我想答应也应该明白我们定时器的启动是在构造函数中做的,没错,从源码中我们可以得道验证:

多线程之----定时器TIMER

哈哈哈  没错吧,其实我们是Timer是一个单独的线程,从第152行我们就可以看到,我们可以设置线程的名称,可以设置是否是守护线程,然后调用start方法定时器就起作用了。但是并不会立即执行。线程调用start后也不会立即执行,这在上一篇中已经有说到了,他其实是把当前的线程示例放到了一个线程组中等待被执行。

哪我们就看他是如何调度的也就是schdule是如何执行的呢。

多线程之----定时器TIMER多线程之----定时器TIMER

 

 

 这里我们补充一下,queue是一个TaskQueue,

好了对Timer的介绍今天就到这里吧,如有没有说到的请各位评论一下,大家一块学习。