定時器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();
}
};