java类库的阅读笔记_jdk1.7.0_40_java.util.Timer

来源:互联网 发布:淘宝客服手册 编辑:程序博客网 时间:2024/06/12 20:35

2013 1116:

类:

java.util.Timer


属性:

private final TaskQueue queue = new TaskQueue();

笔记:

一个定时器可以支持多个定时任务,定时任务的存储就放在queue中。

TaskQueue有一个定时任务数组private TimerTask[] queue = new TimerTask[128]; 这个数组使用最小堆存储定时任务。因此,支持的最大定时任务数目为127 。


属性:

private final TimerThread thread = new TimerThread(queue);

笔记:

TimerThread是一个继承了Thread的类。内部存储了queue属性,这个类的run方法,就是一个while(true)循环,不断的从queue中取出定时任务进行执行。

因为它只是一个线程,支持最多127个定时任务,所以从设计上来讲,不应该在定时任务中执行耗时操作,以免阻塞定时任务的调度。


属性:

private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);

笔记:

这个静态属性用来给Timer命名,如果客户端没有指定Timer的名称,就会用 “Timer-”+nextSerialNumber 来进行命名。nextSerialNumber 每次自增1。

所谓命名,就是给Timer中的thread线程命名。


方法:

四种构造器、public void cancel()、public int purge()

笔记:

四种构造器,可以指定定时器名称,是否守护线程。

cancel方法用于将queue中的任务清空,并且设置TimerThread的newTasksMayBeScheduled属性,从而结束掉线程。

purge方法用于将queue中已经取消的任务清空。


方法:

private void sched(TimerTask task, long time, long period)

笔记:

该方法用于向queue中添加任务。如果period为0,则不是周期任务。time是下次执行时间。

如果period为正数,说明该任务计划在固定间隔执行(因为是一个线程在执行多个任务,实际上可能不是固定时间间隔的,可能被延后,但是延后不影响下次计划执行时间)。

如果period为负数,则该任务计划在上次开始执行时间,间隔period以后执行。


方法:

schedule重载方法、scheduleAtFixedRate重载方法

笔记:

schedule重载方法,即依据实际执行时间进行任务间隔。

scheduleAtFixedRate重载方法,依据计划间隔进行执行,无论任务某次有没有被延迟执行。


类:

java.util.TimerTask

属性:

int state = VIRGIN;

笔记:

该属性取值范围为:VIRGIN、SCHEDULED、EXECUTED、CANCELLED。含义为:初始状态、调度中、执行完成、已取消。

其中VIRGIN为初始值。CANCELLED由cancel方法使用。SCHEDULED是该任务加入Timer的时候赋予的。EXECUTED是任务在TimerThread中判断执行完以后赋予的。

该属性的约束为:只有VIRGIN状态,才允许被加入Timer调度。

事实上,一个TimerTask,只能被调度一次。一旦加入某个Timer,那么它的后续状态必然是非VIRGIN的。


属性:

final Object lock = new Object();

笔记:

由于state属性是由多个线程共享的变量,并且存在竞态条件,因此需要进行锁保护。涉及的线程有:客户端线程调用cancel方法、调用Timer的计划定时任务方法、TimerThread。


属性:

long nextExecutionTime;long period = 0;

笔记:

下次执行时间;执行周期。nextExecutionTime在定时任务计划前被设定,在每次定时任务执行前,被重新计算。TimerTask是以最小堆形式存储的,其关键字就是nextExecutionTime,因此在TimerTask移动中,被读取用来进行比较。


类:

java.util.TimerThread

属性:

boolean newTasksMayBeScheduled = true;

笔记:

用来标识TimerThread的运行状态。

true代表TimerThread可以进行定时调度或者等待新的任务。

false代表Timer被取消或者TimerThread发生异常已经被迫结束。


属性:

private TaskQueue queue;

笔记:

即待调度的定时任务队列。


方法:

private void mainLoop()

笔记:

该方法用来进行定时任务调度。

1、因为调度的时候,需要从queue队列中取任务,需要判断任务状态,因此里面使用了两把锁:queue和task.lock,分别与Timer客户端、TaskTimer客户端进行互斥。

2、该死循环正常结束条件为:cancel方法被调用后,queue为空,且newTasksMayBeScheduled 属性为false。异常结束条件自然是异常。

3、该方法的外层,有finally保护,无论因何种情况线程结束,作为定时线程状态的newTasksMayBeScheduled 都会被置为false,queue都会被清空。

4、定时调度的方法是,最小堆的堆顶,是最快要被执行的任务,可以计算出它的计划执行时间nextExecutionTime和当前时间的差值,并利用queue.wait(long time)来进行线程等待。之所以用queue,是因为它作为Timer的一个属性,可以监控Timer的cancel方法和sche方法,从而在必要的时候,唤醒TimerThread,重新进行最小堆计算,或者退出线程。


好了,很简单的Timer主线代码看完了,现在来看看牛逼的代码。。。

类:

java.util.Timer

属性:

private final Object threadReaper = new Object()

笔记:

TimerThread中的mainLoop里面,有两个地方调用了wait方法,一处是没有计划任务时,线程等待新的任务进来。一处是有计划任务,等待计划任务所设定的执行时间唤醒。

第二种情况没什么问题,即使开启Timer的客户端线程已经退出了,这些定时任务还是需要一次又一次的运行。

但是第一种情况有风险。如果客户端new了一个Timer,但是没有往里面放任务,也不cancel就退出了。那么TimerThread就会停在wait这里,没有清除的机会了。

所以为了解决这种情况(客户端忘记Timer.cancel)下的泄露,只能使用一些特殊的手段。

一个客户端忘记cancel的Timer,和一个闲置但是可能被用到的Timer有什么区别呢?

它们唯一的区别就是,后者仍保持着Timer的引用,而前者随着客户端退出,Timer成为了待回收的对象。一个可回收的对象,在被回收之前,有一次执行代码的机会,这个机会就是传说中的:

protected void finalize() throws Throwable

那么就在这个方法中,唤醒TimerThread吧:

protected void finalize() throws Throwable {            synchronized(queue) {                thread.newTasksMayBeScheduled = false;                queue.notify(); // In case queue is empty.            }        }
正如Effective Java中所写的那样,直接将finalize方法写在Timer中,是不合适的。

一种会导致错误的情况是,子类覆盖了Timer的finalize方法,但是忘记了调用super.finalize,或者因为程序错误没有执行super.finalize,或者直接是一个恶意的子类,那么Timer的finalize方法就得不到执行机会。

解决方法有两种:

1、finalize方法仍然直接写在Timer中,但是加上final关键字,不允许子类覆盖。作为一个公共接口,这种做法多多少少有些差强人意。

2、另一种就是使用Effective Java中推荐的做法,在Timer中保存一个私有属性,并为该对象配置上面的finalize方法。

方法2简单有效。

由于是私有属性threadReaper,所以避免了子类覆盖所引发的问题。

由于threadReaper仅仅被Timer对象引用,所以Timer对象可回收,代表着threadReaper可以被回收,在触发finalize时机上一致了。

另外,作为私有属性,这个finalize方法仍然具有Timer一切属性方法的访问权限,写代码上也没有任何阻碍。

因此,请膜拜神一样的代码:

    private final Object threadReaper = new Object() {        protected void finalize() throws Throwable {            synchronized(queue) {                thread.newTasksMayBeScheduled = false;                queue.notify(); // In case queue is empty.            }        }    };




原创粉丝点击