1. 程式人生 > >JAVA的定時器總結

JAVA的定時器總結



java的定時器在平時應用到的還是很多的,比如它可以定時清理資料、定時清理快取、定時彈出訊息提示等等。我們現在做的一個專案應用的就比較多,我們主要應用它來監測程式的執行狀態。用的地方這麼多,那就有必要整理一下,翻了翻API,也上網查了查資料,發現了一個問題,網上有些人說Timer類的scheduleAtFixedRate()方法存在著執行緒同步的問題,可是API上介紹的Timer類是執行緒安全類啊。覺得誰的都不敢相信,還是自己驗證一下比較好。

既然已經用到了Timer,那麼就把基礎的用法順便也整理一下,首先寫個最簡單的定時器例子:

[java] view plaincopyprint?
在CODE上檢視程式碼片派生到我的程式碼片
  1. import java.io.IOException;  
  2. import java.text.Format;  
  3. import java.text.ParseException;  
  4. import java.text.SimpleDateFormat;  
  5. import java.util.Date;  
  6. import java.util.Timer;  
  7. import java.util.TimerTask;  
  8. /** 
  9.  * Timer是一種執行緒設施,用於安排以後在後臺執行緒中執行的任務。可安排任務執行一次,或者定期重複執行。 
  10.  * Timer學習比較簡單,按照API把Timer類中的方法弄懂就夠用了。
     
  11.  *  
  12.  * 此類是執行緒安全的:多個執行緒可以共享單個 Timer 物件而無需進行外部同步。   
  13.  * 這一點很重要(即使是使用scheduleAtFixedRate方法的時候碰見延遲問題,也不會出現執行緒同步的問題) 
  14.  *  
  15.  * schedule()方法有四種引數形式: 
  16.  *  
  17.  * (1)、void schedule(TimerTask task, Date time)   安排在指定時間執行指定任務(如果傳遞的引數時間已過,則立即執行任務) 
  18.  *  
  19.  * (2)、void schedule(TimerTask task, long delay)  安排在指定延遲後執行指定任務 
  20.  * 
     
  21.  * (3)、void schedule(TimerTask task, Date firstTime, long period)  安排指定的任務在指定的時間開始進行重複的固定延遲執行。 
  22.  *      在固定延遲執行中,根據前一次執行的實際執行時間來安排每次執行。如果由於任何原因(如垃圾回收或其他後臺活動)而延遲了某次執行, 
  23.  *      則後續執行也將被延遲。從長期來看,執行的週期一般要略大於指定的週期。 
  24.  *  
  25.  * (4)、schedule(TimerTask task, long delay, long period) 安排指定的任務在指定延遲後開始進行重複的固定延遲執行。 
  26.  *      <span style="white-space: pre;">    </span>在固定延遲執行中,根據前一次執行的實際執行時間來安排每次執行。如果由於任何原因(如垃圾回收或其他後臺活動)而延遲了某次執行, 
  27.  *      則後續執行也將被延遲。從長期來看,執行的週期一般要略大於指定的週期。 
  28.  *  
  29.  * scheduleAtFixedRate()方法有兩種引數形式   
  30.  *      在固定速率執行中,相對於已安排的初始執行時間來安排每次執行。如果由於任何原因(如垃圾回收或其他背景活動)而延遲了某次執行, 
  31.  *      則將快速連續地出現兩次或更多次執行,從而使後續執行能夠趕上來。從長遠來看,執行的頻率將正好是指定週期的倒數 
  32.  *  
  33.  * (1)、 void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 安排指定的任務在指定的時間開始進行重複的"固定速率"執行。 
  34.  *  
  35.  * (2)、void scheduleAtFixedRate(TimerTask task, long delay, long period)     安排指定的任務在指定的延遲後開始進行重複的"固定速率"執行。 
  36.  *  
  37.  * cancel() 方法:終止此計時器,丟棄所有當前已安排的任務。這不會干擾當前正在執行的任務(就是說已經進入run()方法的了就繼續執行,如果還有其他排隊的,那不執行)。 
  38.  *     <span style="white-space: pre;"> </span>  一旦終止了計時器,那麼它的執行執行緒也會終止,並且無法根據它安排更多的任務。  
  39.  *  
  40.  * int purge()    從此計時器的任務佇列中移除所有已取消的任務,如果沒有對這些任務的外部引用,則它們就成為垃圾回收的合格物件。   以時間換取了空間。  一般程式不必要用。 
  41.  *            返回值是從佇列中移除的任務數。 
  42.  * 
  43.  */
  44. publicclass TimerTest {  
  45.     publicstaticvoid main(String[] args) throws ParseException {  
  46.         final Timer timer = new Timer();  
  47.         timer.schedule(new TimerTask(){  
  48.             @Override
  49.             publicvoid run() {  
  50.                 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  51.                 Date date1=new Date();  
  52.                 String t1=df.format(date1);//取到格式化後的當前時間
  53.                 String t2="2013-12-17 21:00:00";  
  54.                 Date date2 = null;  
  55.                     try {  
  56.                         date2 = df.parse(t2);//將字串轉換成時間格式
  57.                     } catch (ParseException e) {  
  58.                         // TODO Auto-generated catch block
  59.                         e.printStackTrace();  
  60.                     }  
  61.                 System.out.println(date1.getTime());  
  62.                 //指定定時器結束的條件,要不然會該定時器會一直執行下去
  63.                 if(date1.getTime()>=date2.getTime()){  
  64.                     System.out.println(t1+"定時器停止了");  
  65.                     timer.cancel();//終止該定時器
  66.                 }  
  67.             }  
  68.         }, 1000,2000);// 在1秒後執行此任務,每次間隔2秒
  69.         }  
  70.     }  
import java.io.IOException;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
 * Timer是一種執行緒設施,用於安排以後在後臺執行緒中執行的任務。可安排任務執行一次,或者定期重複執行。
 * Timer學習比較簡單,按照API把Timer類中的方法弄懂就夠用了。
 * 
 * 此類是執行緒安全的:多個執行緒可以共享單個 Timer 物件而無需進行外部同步。  
 * 這一點很重要(即使是使用scheduleAtFixedRate方法的時候碰見延遲問題,也不會出現執行緒同步的問題)
 * 
 * schedule()方法有四種引數形式:
 * 
 * (1)、void schedule(TimerTask task, Date time)   安排在指定時間執行指定任務(如果傳遞的引數時間已過,則立即執行任務)
 * 
 * (2)、void schedule(TimerTask task, long delay)  安排在指定延遲後執行指定任務
 * 
 * (3)、void schedule(TimerTask task, Date firstTime, long period)  安排指定的任務在指定的時間開始進行重複的固定延遲執行。
 * 		在固定延遲執行中,根據前一次執行的實際執行時間來安排每次執行。如果由於任何原因(如垃圾回收或其他後臺活動)而延遲了某次執行,
 * 		則後續執行也將被延遲。從長期來看,執行的週期一般要略大於指定的週期。
 * 
 * (4)、schedule(TimerTask task, long delay, long period) 安排指定的任務在指定延遲後開始進行重複的固定延遲執行。
 *   	<span style="white-space: pre;">	</span>在固定延遲執行中,根據前一次執行的實際執行時間來安排每次執行。如果由於任何原因(如垃圾回收或其他後臺活動)而延遲了某次執行,
 * 		則後續執行也將被延遲。從長期來看,執行的週期一般要略大於指定的週期。
 * 
 * scheduleAtFixedRate()方法有兩種引數形式  
 * 		在固定速率執行中,相對於已安排的初始執行時間來安排每次執行。如果由於任何原因(如垃圾回收或其他背景活動)而延遲了某次執行,
 * 		則將快速連續地出現兩次或更多次執行,從而使後續執行能夠趕上來。從長遠來看,執行的頻率將正好是指定週期的倒數
 * 
 * (1)、 void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 安排指定的任務在指定的時間開始進行重複的"固定速率"執行。
 * 
 * (2)、void scheduleAtFixedRate(TimerTask task, long delay, long period)     安排指定的任務在指定的延遲後開始進行重複的"固定速率"執行。
 * 
 * cancel() 方法:終止此計時器,丟棄所有當前已安排的任務。這不會干擾當前正在執行的任務(就是說已經進入run()方法的了就繼續執行,如果還有其他排隊的,那不執行)。
 * 	   <span style="white-space: pre;">	</span>  一旦終止了計時器,那麼它的執行執行緒也會終止,並且無法根據它安排更多的任務。 
 * 
 * int purge()    從此計時器的任務佇列中移除所有已取消的任務,如果沒有對這些任務的外部引用,則它們就成為垃圾回收的合格物件。   以時間換取了空間。  一般程式不必要用。
 * 	          返回值是從佇列中移除的任務數。
 *
 */
public class TimerTest {
	public static void main(String[] args) throws ParseException {
		final Timer timer = new Timer();
		timer.schedule(new TimerTask(){
			@Override
			public void run() {
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				Date date1=new Date();
				String t1=df.format(date1);//取到格式化後的當前時間
				
				String t2="2013-12-17 21:00:00";
				Date date2 = null;
					try {
						date2 = df.parse(t2);//將字串轉換成時間格式
					} catch (ParseException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				System.out.println(date1.getTime());
				
				//指定定時器結束的條件,要不然會該定時器會一直執行下去
				if(date1.getTime()>=date2.getTime()){
					System.out.println(t1+"定時器停止了");
					timer.cancel();//終止該定時器
				}
			}
		}, 1000,2000);// 在1秒後執行此任務,每次間隔2秒
		}
	}
	

基礎的用法解釋都在程式碼的註釋當中。我們著重要說的是schedure 與 schedureAtFixeRate這兩個方法的區別以及schedureAtFixeRate方法是否存在同步問題 :

schedure (TimeTask task, Date t1, long period ) 方法在執行任務的時候如果某一任務執行過程中發生延遲,那麼下面的任務時間都會順延,也就是說,下一任務的啟動是根據前一任務執行結束的時間 + 指定的間隔週期。

schedureAtFixeRate(TimeTask task, Date t1, long period ) 方法中一個任務的啟動時間是根據前一任務的開始時間+指定的間隔週期。如果某一任務執行過程中發生延遲,那他後面的任務會迅速挨個執行,直到任務間隔週期正常。

注意後面的任務是迅速挨個執行的,而不是一起執行。

看個程式碼例項:

[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. import java.util.Date;  
  2. import java.util.Timer;  
  3. import java.util.TimerTask;  
  4. /** 
  5.  * 演示scheduleAtFixedRate與schedule的區別 
  6.  * 證明scheduleAtFixedRate方法即使發生延遲的時候也是執行緒安全的。 
  7.  * @author HaiCheng 
  8.  */
  9. publicclass TimerTest2 {  
  10.     publicstaticvoid main(String[] args) {  
  11.         final Timer  t1= new Timer();  
  12.         t1.schedule(new TimerTask() {  
  13.             int count=0;  
  14.             long flag=0;//時間標記
  15.             @Override
  16.             publicvoid run() {  
  17.                 if(count!=0){  
  18.                 System.out.println("間隔時間---->"+(new Date().getTime()-flag)+"----count的值---->"+count);  
  19.                 }  
  20.                 flag=new Date().getTime();//上面那行先執行,下面這行後執行的  時間會略有偏差 ,差不多2毫秒左右
  21.                 count++;  
  22.                 if(count==3){  
  23.                     try {  
  24.                         Thread.sleep(2000);  
  25.                     } catch (InterruptedException e) {  
  26.                         // TODO Auto-generated catch block
  27.                         e.printStackTrace();  
  28.                     }  
  29.                 }  
  30.                 if(count==25){  
  31.                     t1.cancel();  
  32.                 }  
  33.             }  
  34.         }, 0100);  
  35.     }  
  36. }  
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 演示scheduleAtFixedRate與schedule的區別
 * 證明scheduleAtFixedRate方法即使發生延遲的時候也是執行緒安全的。
 * @author HaiCheng
 */
public class TimerTest2 {
	
	public static void main(String[] args) {
		final Timer  t1= new Timer();
		t1.schedule(new TimerTask() {
			int count=0;
			long flag=0;//時間標記
			@Override
			public  void run() {
				if(count!=0){
				System.out.println("間隔時間---->"+(new Date().getTime()-flag)+"----count的值---->"+count);
				}
				flag=new Date().getTime();//上面那行先執行,下面這行後執行的  時間會略有偏差 ,差不多2毫秒左右
				count++;
				if(count==3){
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				if(count==25){
					t1.cancel();
				}
			}
		}, 0, 100);
	}
}

執行結果: [java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. 間隔時間---->100----count的值---->1
  2. 間隔時間---->100----count的值---->2
  3. 間隔時間---->2000----count的值---->3
  4. 間隔時間---->100----count的值---->4
  5. 間隔時間---->100----count的值---->5
  6. 間隔時間---->100----count的值---->6
  7. 間隔時間---->100----count的值---->7
  8. 間隔時間---->100----count的值---->8
  9. 間隔時間---->100----count的值---->9
  10. 間隔時間---->100----count的值---->10
  11. 間隔時間---->100----count的值---->11
  12. 間隔時間---->100----count的值---->12
  13. 間隔時間---->100----count的值---->13
  14. 間隔時間---->100----count的值---->14
  15. 間隔時間---->100----count的值---->15
  16. 間隔時間---->100----count的值---->16
  17. 間隔時間---->100----count的值---->17
  18. 間隔時間---->100----count的值---->18
  19. 間隔時間---->101----count的值---->19
  20. 間隔時間---->100----count的值---->20
  21. 間隔時間---->100----count的值---->21
  22. 間隔時間---->100----count的值---->22
  23. 間隔時間---->100----count的值---->23
  24. 間隔時間---->100----count的值---->24
間隔時間---->100----count的值---->1
間隔時間---->100----count的值---->2
間隔時間---->2000----count的值---->3
間隔時間---->100----count的值---->4
間隔時間---->100----count的值---->5
間隔時間---->100----count的值---->6
間隔時間---->100----count的值---->7
間隔時間---->100----count的值---->8
間隔時間---->100----count的值---->9
間隔時間---->100----count的值---->10
間隔時間---->100----count的值---->11
間隔時間---->100----count的值---->12
間隔時間---->100----count的值---->13
間隔時間---->100----count的值---->14
間隔時間---->100----count的值---->15
間隔時間---->100----count的值---->16
間隔時間---->100----count的值---->17
間隔時間---->100----count的值---->18
間隔時間---->101----count的值---->19
間隔時間---->100----count的值---->20
間隔時間---->100----count的值---->21
間隔時間---->100----count的值---->22
間隔時間---->100----count的值---->23
間隔時間---->100----count的值---->24
將上面例子的schedule方法換成scheduleAtFixedRate  執行結果變為: [java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. 間隔時間---->100----count的值---->1
  2. 間隔時間---->100----count的值---->2
  3. 間隔時間---->2000----count的值---->3
  4. 間隔時間---->0----count的值---->4
  5. 間隔時間---->0----count的值---->5
  6. 間隔時間---->0----count的值---->6
  7. 間隔時間---->0----count的值---->7
  8. 間隔時間---->0----count的值---->8
  9. 間隔時間---->0----count的值---->9
  10. 間隔時間---->0----count的值---->10
  11. 間隔時間---->0----count的值---->11
  12. 間隔時間---->0----count的值---->12
  13. 間隔時間---->0----count的值---->13
  14. 間隔時間---->0----count的值---->14
  15. 間隔時間---->0----count的值---->15
  16. 間隔時間---->0----count的值---->16
  17. 間隔時間---->0----count的值---->17
  18. 間隔時間---->0----count的值---->18
  19. 間隔時間---->0----count的值---->19
  20. 間隔時間---->0----count的值---->20
  21. 間隔時間---->0----count的值---->21
  22. 間隔時間---->0----count的值---->22
  23. 間隔時間---->98----count的值---->23
  24. 間隔時間---->100----count的值---->24
間隔時間---->100----count的值---->1
間隔時間---->100----count的值---->2
間隔時間---->2000----count的值---->3
間隔時間---->0----count的值---->4
間隔時間---->0----count的值---->5
間隔時間---->0----count的值---->6
間隔時間---->0----count的值---->7
間隔時間---->0----count的值---->8
間隔時間---->0----count的值---->9
間隔時間---->0----count的值---->10
間隔時間---->0----count的值---->11
間隔時間---->0----count的值---->12
間隔時間---->0----count的值---->13
間隔時間---->0----count的值---->14
間隔時間---->0----count的值---->15
間隔時間---->0----count的值---->16
間隔時間---->0----count的值---->17
間隔時間---->0----count的值---->18
間隔時間---->0----count的值---->19
間隔時間---->0----count的值---->20
間隔時間---->0----count的值---->21
間隔時間---->0----count的值---->22
間隔時間---->98----count的值---->23
間隔時間---->100----count的值---->24

通過執行結果間隔時間的區別我們很容易區別開這兩個方法的使用。

通過count的值打印出的順序是對的,沒有重複的,說明scheduleAtFixedRate  方法的任務發生延遲的時候也不會出現執行緒安全問題。