定时器Timer
-
Timer timer = new Timer(true);将定时器设置为守护线程(daemon),即当用户线程都已经执行完毕退出以后,jvm就会结束守护进程,不管守护进程是否还有任务,程序退出。
定时器任务TimerTask
-
可以由计时器进行一次性或重复执行的任务,简单来说就是计时器要执行的任务。
ok,把两者联系起来使用就是:
timer.schedule(TimerTask task, long delay, long period)
在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。
还有更多API也顺便在这里copy出来,看的时候方便:
-
-
void
cancel()
终止此计时器,丢弃任何当前计划的任务。
int
purge()
从该计时器的任务队列中删除所有取消的任务。
void
schedule(TimerTask task, Date time)
在指定的时间安排指定的任务执行。
void
schedule(TimerTask task, Date firstTime, long period)
从指定 的时间开始 ,对指定的任务执行重复的 固定延迟执行 。
void
schedule(TimerTask task, long delay)
在指定的延迟之后安排指定的任务执行。
void
schedule(TimerTask task, long delay, long period)
在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。
void
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
从指定的时间 开始 ,对指定的任务执行重复的 固定速率执行 。
void
scheduleAtFixedRate(TimerTask task, long delay, long period)
在指定的延迟之后 开始 ,重新执行 固定速率的指定任务。
-
其实timer相当于一个后台单线程,timerTask就是timer里面的单个线程任务,timer除非设置成守护线程或则主动使用cancle方法终止计时器,否则会一直执行,而其中独立的timerTask也可以单独调用timerTask.cancle()方法终止任务,但是此方法只是终止了timer其中的某个独立的任务,timer仍然会继续执行:
例如:
Timer timer = new Timer();
TimerTask tt1 = new TimerTask(){public void run(){//执行代码}};
TimerTask tt2 = new TimerTask(){public void run(){//执行代码}};
timer.schedule(tt1,new Date(),5000);
timer.schedule(tt2,1000,5000);
调用其中的tt1的cancle方法只会终止tt1的任务,而timer和tt2任然会继续执行。
【附加说明】串行化特性:
这样就是把tt1和tt2两个任务放入了timer线程当中串行执行,即tt1执行完才轮到tt2执行,t可以看到tt1和tt2其实应该是同时执行的,但是tt2延迟一秒钟期间,如果tt1还未执行完毕,那么tt2的开始执行时间因而会被推迟到tt1执行完毕后才开始执行,这就是为什么有时候发现为什么没有按时执行,可以从这里分析一下问题原因。
----timer单线程
-
对应于每个Timer对象是单个后台线程,用于依次执行所有定时器的所有任务。
public class TestTImerCircle2 {
public static void main(String[] args) throws InterruptedException {
Timer t = new Timer();
//第一个任务
TimerTask tt1 = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running1");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("running1 end");
}
};
//第二个任务
TimerTask tt2 = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running2");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("running2 end");
}
};
t.schedule(tt2, new Date());
t.schedule(tt1, new Date());
}
}
可以看到输出:
running2
running2 end
running1
running1 end程序未终止
running2 end执行在running1之前,如果是多线程并行running1会在running2 end之前,此处证明timer是单个线程后台。
需要区别与多线程并行的线程池执行任务:
public class ThreadPoolTest{
public static void main(String[] args) throws InterruptedException {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//第一个任务
Runnable tt1 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running1");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("running1 end");
}
};
//第二个任务
Runnable tt2 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running2");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("running2 end");
}
};
newCachedThreadPool.submit(tt1);
newCachedThreadPool.submit(tt2);
newCachedThreadPool.shutdown();
}
}
如上,tt1和tt2是同时进行的,输出结果是:
running2
running1
running1 end
running2 end程序终止
----timer的终止
-
空引用回收:在最后一次对Timer对象的引用后,所有未完成的任务已完成执行,定时器的任务执行线程正常终止(并被收集到垃圾回收)。 但是,这可能需要任意长时间的发生。
-
主动终止计时器:如果主叫方想要快速终止定时器的任务执行线程,则调用者应该调用定时器的cancel方法。
public class TestTImerCircle {
public static void main(String[] args) {
Timer t = new Timer();
TimerTask tt = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running");
}
};
t.schedule(tt, new Date());
//方法一:置空等待回收
t = null;
//方法二:cancle终止
//t.cancle();
System.gc();
}
}
执行结果:
running
程序终止
以上,如果注释掉最后一行代码,发现很久(非常久,测试几个钟结束)都没有反应,因为jvm回收垃圾发生在任何时候,而主动调用gc回收发现程序立马终止,验证以上结论1。注释掉t=null,取消注释t.cancle()发现running并没有输出而是直接被终止,这是因为任务还没开始执行就被终止了,使用以下代码可以看到输出running:
public class TestTImerCircle2 {
public static void main(String[] args) throws InterruptedException {
Timer t = new Timer();
TimerTask tt = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running");
}
};
t.schedule(tt, new Date());
Thread.sleep(100);
t.cancel();
// t = null;
// System.gc();
}
}
执行结果:
running
程序终止
最重要的一种情况需要注意,如果任务正在执行,还未执行完,那么cancle()将不会引起正在进行的任务立马结束,而是等待当前任务完成以后,才终止计时器:
public class TestTImerCircle2 {
public static void main(String[] args) throws InterruptedException {
Timer t = new Timer();
TimerTask tt = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("running end");
}
};
t.schedule(tt, new Date());
Thread.sleep(100);
t.cancel();
// t = null;
// System.gc();
}
}
执行结果:
running
等待2秒钟
running end
程序终止
可以看到,两秒后才输出running end,另外cancle一旦触发,不管是否设置了重复执行,只要当前没有正在执行中的任务,立刻终止计时器:
public class TestTImerCircle2 {
public static void main(String[] args) throws InterruptedException {
Timer t = new Timer();
TimerTask tt = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("running end");
}
};
t.schedule(tt, new Date(),1);
Thread.sleep(100);
t.cancel();
// t = null;
// System.gc();
}
}
执行结果:
running
执行2秒钟后
running end
程序终止
以上设置了隔1毫秒执行一次,但是执行完第一次后程序立刻被结束退出了,此处证明cancle具有延迟生效特性(有正在执行的任务)和必定执行特性(代码一旦被执行,必定在下一次任务前终止计时器)。
以上就介绍这么多,timer和timertask的使用应该足够了,然后还有一个是purge这个方法,从该计时器的任务队列中删除所有取消的任务,返回删除数,不知道为什么测试总是0,后续有空再更新测试吧。
对了,以上例子因为使用了timer.schedule(TimerTask timerTask,Date time)这个方法,在指定的时间安排指定的任务执行,并且只执行一次,所以如果碰到需要使用timer.schedule(TimerTask timerTask,Date time,long period)这种方法来重复间隔执行任务的情况,而达到条件要取消掉其中的一个timertask,那就可以在timertask的实现方法run中加入this.cancle()即可把timer中的该任务取消掉了,接着应该就可以使用timer.purge()这个方法清除掉,也可以不使用,等待jvm回收即可。
TimerTask tt2 = new TimerTask() {
@Override
public void run() {
// 达到条件,结束
this.cancle();
}
};