1. 程式人生 > >Java多執行緒學習筆記20之定時器Timer

Java多執行緒學習筆記20之定時器Timer

詳細程式碼見:github程式碼地址

 

本節內容:

    定時器Timer的使用及分析

1) 如何實現指定時間執行任務
2) 如何實現按指定週期執行任務

 

第五章

定時器Timer

定時/計劃功能在移動開發領域使用較多,比如Android技術、定時計劃任務功能
在Java中的主要使用的就是Timer物件,它的內部還是使用多執行緒的方式進行處
理,所以它和執行緒技術還是有很大的關聯的。
本章主要掌握以下技術點:
1) 如何實現指定時間執行任務
2) 如何實現按指定週期執行任務

定時器Timer的簡介:


在JDK庫中,Timer類主要負責計劃任務的功能,也就是在指定的時間開始執行某
一個任務Timer.Timer類的主要作用就是設定任務計劃,但封裝任務的類卻是
TimerTask類。執行計劃任務的程式碼要放入TimerTask的子類中,因為TimerTas
k是一個抽象類。

文件翻譯:
Timer類(要注意預設情況下是個非守護執行緒):

public class Timer
extends Object
A facility for threads to schedule tasks for future execution in a background thread. 
Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals.
Corresponding to each Timer object is a single background thread that is used to 
execute all of the timer's tasks, sequentially. Timer tasks should complete quickly. 
If a timer task takes excessive time to complete, it "hogs" the timer's task execution 
thread. This can, in turn, delay the execution of subsequent tasks, which may "bunch up"
and execute in rapid succession when (and if) the offending task finally completes.
用於執行緒排程任務,並在後臺未來中執行的一個工具。
任務可以被安排為因此執行,也可以安排為定期重複執行。對應於每個計數器物件的是一個使用的單一後臺執行緒,
按順序執行計數器的所有任務。計時器任務應該很快完成,如果一個計時器任務需要過多的時間來完成,它會佔用
計時器任務的執行時間。這反過來又會延遲後續任務的執行,如果惱人的任務最重完成時,後續任務可能會"堆積"
並迅速連續執行。

After the last live reference to a Timer object goes away and all outstanding tasks have 
completed execution, the timer's task execution thread terminates gracefully (and becomes 
subject to garbage collection). However, this can take arbitrarily long to occur. By default, 
the task execution thread does not run as a daemon thread, so it is capable of keeping an 
application from terminating. If a caller wants to terminate a timer's task execution thread 
rapidly, the caller should invoke the timer's cancel method.
在對計時器物件的最後一次實時引用消失且所有未完成的任務都已完成執行之後,計時器的任務執行執行緒將優雅地
終止並且成為垃圾收集的物件。然而,這可能需要任意長的時間。預設情況下,任務執行執行緒不作為守護執行緒執行,因此
它能夠防止應用程式終止。如果呼叫者希望快速終止計時器的任務執行執行緒,則呼叫方應該呼叫計時器的cancel方法。

If the timer's task execution thread terminates unexpectedly, for example, because its stop
method is invoked, any further attempt to schedule a task on the timer will result in an 
IllegalStateException, as if the timer's cancel method had been invoked.
例如,如果計時器的任務執行執行緒意外終止,因為呼叫了它的停止方法,那麼在計時器上安排一個任務的嘗試將丟擲
IllegalStateException異常,就好像計時器的cancel方法被觸發一樣。

This class is thread-safe: multiple threads can share a single Timer object without the 
need for external synchronization.
這個類是執行緒安全的: 多個執行緒可以共享一個計時器物件,而不需要外部的同步。

This class does not offer real-time guarantees: it schedules tasks using the Object.wait(long) method.
這個類不提供實時保證: 它使用Object.wait(long)方法排程任務。

Java 5.0 introduced the java.util.concurrent package and one of the concurrency utilities 
therein is the ScheduledThreadPoolExecutor which is a thread pool for repeatedly executing 
tasks at a given rate or delay. It is effectively a more versatile replacement for the 
Timer/TimerTask combination, as it allows multiple service threads, accepts various time 
units, and doesn't require subclassing TimerTask (just implement Runnable). Configuring 
ScheduledThreadPoolExecutor with one thread makes it equivalent to Timer.
Java5.0 引入了併發的包,以及一個併發工具ScheduledThreadPoolExecutor,它是一個執行緒池,用於以給定
速率或延遲重複執行任務。它可以有效地替代計時器/TimerTask組合,因為它允許多個服務執行緒接受不同的單元,
並且不需要子類化TimerTask(只需要實現Runnable).使用一個執行緒配置ScheduledThreadPoolExecutor就相當於
計時器。

Implementation note: This class scales to large numbers of concurrently scheduled tasks 
(thousands should present no problem). Internally, it uses a binary heap to represent its 
task queue, so the cost to schedule a task is O(log n), where n is the number of 
concurrently scheduled tasks.
實現注意: 這個類可以擴充套件到大量併發排程任務(數千個任務應該沒有問題)。在內部,它使用一個二進位制堆來表示
其任務佇列,因此排程任務的成本是O(logn),其中n是併發排程任務的數量

Implementation note: All constructors start a timer thread.
實現注意:所有建構函式啟動一個計時器執行緒。

TimerTask類:

public abstract class TimerTask
extends Object
implements Runnable
A task that can be scheduled for one-time or repeated execution by a Timer.
A timer task is not reusable. Once a task has been scheduled for execution on a Timer or 
cancelled, subsequent attempts to schedule it for execution will throw IllegalStateException.
一個任務可以由計數器安排執行一次或重複執行。一旦一個任務被安排執行在一個計時器或取消了,這個計時器任務
不可重用,隨後的嘗試安排它執行將丟擲IllegalStateException異常

(1) 方法schedule(TimerTask task, Data time)的測試
該方法的作用是在指定的日期執行一次某一任務

1) 執行任務的時間晚於當前時間--在未來執行的效果
 

package chapter05.section1.thread_5_1_1.project_1_timerTest1;

import java.util.Date;
import java.util.TimerTask;

public class MyTask extends TimerTask {
	
	@Override
	public void run() {
		System.out.println("任務執行了, 時間為: " + new Date());
	}
}


package chapter05.section1.thread_5_1_1.project_1_timerTest1;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test1 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.add(Calendar.SECOND, 10);
		Date runDate = calendarRef.getTime();
		
		MyTask task = new MyTask();
		Timer timer = new Timer();
		timer.schedule(task, runDate);
	}
}
/*
result:
當前時間為: Sat Nov 20 13:09:39 CST 2017
任務執行了, 時間為: Sat Nov 20 13:09:49 CST 2017
可以看到還在執行
*/

package chapter05.section1.thread_5_1_1.project_1_timerTest1;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test2 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.add(Calendar.SECOND, 10);
		Date runDate = calendarRef.getTime();
		
		MyTask task = new MyTask();
		//守護執行緒
		Timer timer = new Timer(true);
		timer.schedule(task, runDate);
	}
}
/*
result:
當前時間為: Sat Nov 20 13:09:39 CST 2017
可以看到程序直接結束了,因為當程序中不存在非守護執行緒了,則守護執行緒自動
銷燬
*/

結果分析:
任務雖然執行完了,但程序還未銷燬,呈紅色狀態


可以看Timer類中的如下程式碼:

下面是呼叫有參構造函數了,然後利用原子類AtomicInteger設定計時器名稱
    /**
     * This ID is used to generate thread names.
     */
    private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
    private static int serialNumber() {
        return nextSerialNumber.getAndIncrement();
    }

    /**
     * Creates a new timer.  The associated thread does <i>not</i>
     * {@linkplain Thread#setDaemon run as a daemon}.
     */
    public Timer() {
        this("Timer-" + serialNumber());
    }
這是有參構造器,可以看到是啟動一個新的執行緒,但是注意這個執行緒的非守護特
性是從main執行緒中獲得的。
 public Timer(String name) {
        thread.setName(name);
        thread.start();
    }

即,建立1個Timer就是啟動1個新的執行緒,那麼這個新啟動的執行緒並不是守護執行緒
,一直在執行。

 

2) 執行時間早於當前時間--提前執行的效果
如果執行任務的時間早於當前時間,則立即執行task任務

package chapter05.section1.thread_5_1_1.project_2_timerTest2;

import java.util.Date;
import java.util.TimerTask;

public class MyTask extends TimerTask {
	
	@Override
	public void run() {
		System.out.println("任務執行了, 時間為: " + new Date());
	}
}


package chapter05.section1.thread_5_1_1.project_2_timerTest2;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test1 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.add(Calendar.SECOND, calendarRef.get(Calendar.SECOND) - 10);
		Date runDate = calendarRef.getTime();
		System.out.println("計劃時間為: " + runDate);
		
		MyTask task = new MyTask();
		Timer timer = new Timer();
		timer.schedule(task, runDate);
	}
}
/*
result:
當前時間為: Sat Nov 03 14:24:17 CST 2018
計劃時間為: Sat Nov 03 14:24:07 CST 2018
任務執行了, 時間為: Sat Nov 03 14:24:17 CST 2018
*/

可以看到任務提前執行了


3) Timer中允許有多個TimerTask任務及延時的測試
我們在上面的程式中建立Test2.java類

package chapter05.section1.thread_5_1_1.project_2_timerTest2;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test2 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef1 = Calendar.getInstance();
		calendarRef1.add(Calendar.SECOND, 10);
		Date runDate1 = calendarRef1.getTime();
		System.out.println("計劃時間為: " + runDate1);
		
		Calendar calendarRef2 = Calendar.getInstance();
		calendarRef2.add(Calendar.SECOND, 15);
		Date runDate2 = calendarRef2.getTime();
		System.out.println("計劃時間為: " + runDate2);
		
		MyTask task1 = new MyTask();
		MyTask task2 = new MyTask();
		Timer timer = new Timer();
		timer.schedule(task1, runDate1);
		timer.schedule(task2, runDate2);
	}
}
/*
result:
當前時間為: Sat Nov 03 14:34:47 CST 2018
計劃時間為: Sat Nov 03 14:34:57 CST 2018
計劃時間為: Sat Nov 03 14:35:02 CST 2018
任務執行了, 時間為: Sat Nov 03 14:34:57 CST 2018
任務執行了, 時間為: Sat Nov 03 14:35:02 CST 2018
*/

結果分析: 
TimerTask是以佇列的方式一個一個被順序地執行,所以執行的時間有可能和預期
的時間不一致,因為前面的時間有可能消耗的時間較長,則後面的任務執行的時間
也被延後


看下面的例子:

package chapter05.section1.thread_5_1_1.project_3_taskLater;

import java.util.Date;
import java.util.TimerTask;

public class MyTaskA extends TimerTask{
	@Override
	public void run() {
		try {
			System.out.println("A begin timer = " + new Date());
			Thread.sleep(20000);
			System.out.println("A   end timer = " + new Date());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}


package chapter05.section1.thread_5_1_1.project_3_taskLater;

import java.util.Date;
import java.util.TimerTask;

public class MyTaskB extends TimerTask{
	@Override
	public void run() {
		System.out.println("B begin timer = " + new Date());
		System.out.println("B   end timer = " + new Date());
	}
}


package chapter05.section1.thread_5_1_1.project_3_taskLater;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test {
	public static void main(String[] args) {
		System.out.println("當前時間為:" + new Date());
		Calendar calendarRef1 = Calendar.getInstance();
		Date runDate1 = calendarRef1.getTime();
		System.out.println("A計劃時間為:" + runDate1);
		
		Calendar calendarRef2 = Calendar.getInstance();
		calendarRef2.add(Calendar.SECOND, 10);
		Date runDate2 = calendarRef2.getTime();
		System.out.println("B計劃時間為:" + runDate2);
		
		MyTaskA task1 = new MyTaskA();
		MyTaskB task2 = new MyTaskB();
		
		Timer timer = new Timer();
		timer.schedule(task1, runDate1);
		timer.schedule(task2, runDate2);		
	}
}
/*
result:
當前時間為:Sat Nov 03 14:42:14 CST 2018
A計劃時間為:Sat Nov 03 14:42:14 CST 2018
B計劃時間為:Sat Nov 03 14:42:24 CST 2018
A begin timer = Sat Nov 03 14:42:14 CST 2018
A   end timer = Sat Nov 03 14:42:34 CST 2018
B begin timer = Sat Nov 03 14:42:34 CST 2018
B   end timer = Sat Nov 03 14:42:34 CST 2018
*/

結果分析:
在程式碼中設定的task1和task2任務的時間間隔是10秒,但是task1需要20秒完成
任務,所以task1結束的時間就是task2開始的時間,不再以10秒做為參考。原理
還是因為Task是放入佇列中的,得一個一個的執行。

(2) 方法schedule(TimerTask task, Date firstTime, long period)的測試
該方法的作用是在指定的日期之後按指定的間隔週期,無限迴圈地執行某一任務

1) 計劃時間晚於當前時間--在未來執行的效果

package chapter05.section1.thread_5_1_2.project_1_timerTest2_period;

import java.util.Date;
import java.util.TimerTask;

public class MyTask extends TimerTask {
	
	@Override
	public void run() {
		System.out.println("任務執行了, 時間為: " + new Date());
	}
}


package chapter05.section1.thread_5_1_2.project_1_timerTest2_period;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test1 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.add(Calendar.SECOND, 10);
		Date runDate = calendarRef.getTime();
		System.out.println("計劃時間為: " + runDate);
		
		MyTask task = new MyTask();
		Timer timer = new Timer();
		timer.schedule(task, runDate, 4000);
	}
}
/*
result:
當前時間為: Sat Nov 03 14:50:40 CST 2018
計劃時間為: Sat Nov 03 14:50:50 CST 2018
任務執行了, 時間為: Sat Nov 03 14:50:50 CST 2018
任務執行了, 時間為: Sat Nov 03 14:50:54 CST 2018
任務執行了, 時間為: Sat Nov 03 14:50:58 CST 2018
任務執行了, 時間為: Sat Nov 03 14:51:02 CST 2018
任務執行了, 時間為: Sat Nov 03 14:51:06 CST 2018
任務執行了, 時間為: Sat Nov 03 14:51:10 CST 2018
*/

每隔4秒執行一次TimerTask任務,並且是無限期重複執行TimerTask任務


2) 計劃時間早於當前時間--提前執行的效果
如果計劃時間早於當前時間,則立即執行task任務

package chapter05.section1.thread_5_1_2.project_1_timerTest2_period;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test2 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.set(Calendar.SECOND, calendarRef.get(Calendar.SECOND) - 10);
		Date runDate = calendarRef.getTime();
		System.out.println("計劃時間為: " + runDate);
		
		MyTask task = new MyTask();
		Timer timer = new Timer();
		timer.schedule(task, runDate, 4000);
	}
}
/*
result:
當前時間為: Sat Nov 03 14:52:15 CST 2018
計劃時間為: Sat Nov 03 14:52:05 CST 2018
任務執行了, 時間為: Sat Nov 03 14:52:15 CST 2018
任務執行了, 時間為: Sat Nov 03 14:52:19 CST 2018
任務執行了, 時間為: Sat Nov 03 14:52:23 CST 2018
*/

可以看到立即執行


3) 任務執行時間被延時

package chapter05.section1.thread_5_1_2.project_2_timerTest2_periodLater;

import java.util.Date;
import java.util.TimerTask;

public class MyTask extends TimerTask{
	@Override
	public void run() {
		try {
			System.out.println("A begin timer = " + new Date());
			Thread.sleep(5000);
			System.out.println("A   end timer = " + new Date());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}


package chapter05.section1.thread_5_1_2.project_2_timerTest2_periodLater;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test1 {
	public static void main(String[] args) {
		System.out.println("當前時間為: " + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.add(Calendar.SECOND, 10);
		Date runDate = calendarRef.getTime();
		System.out.println("計劃時間為: " + runDate);
		
		MyTask task = new MyTask();
		Timer timer = new Timer();
		timer.schedule(task, runDate, 3000);
	}
}
/*
result:
當前時間為: Sat Nov 03 14:56:34 CST 2018
計劃時間為: Sat Nov 03 14:56:44 CST 2018
A begin timer = Sat Nov 03 14:56:44 CST 2018
A   end timer = Sat Nov 03 14:56:49 CST 2018
A begin timer = Sat Nov 03 14:56:49 CST 2018
A   end timer = Sat Nov 03 14:56:54 CST 2018
A begin timer = Sat Nov 03 14:56:54 CST 2018
A   end timer = Sat Nov 03 14:56:59 CST 2018
*/

任務被延時,一個任務執行時間大約5秒,但週期3秒,因此還得一個一個執行。


4) TimerTask類的cancel()方法
TimerTask類的cancel()方法的作用是將自身從任務佇列中進行清除,其他任務不受影響

package chapter05.section1.thread_5_1_2.project_3_timerTestCancelMethod;

import java.util.TimerTask;
import java.util.Date;

public class MyTaskA extends TimerTask{
	@Override
	public void run() {
		System.out.println("A run timer =" + new Date());
		this.cancel();
		System.out.println("A任務自己移除自己");
	}
}


package chapter05.section1.thread_5_1_2.project_3_timerTestCancelMethod;

import java.util.Date;
import java.util.TimerTask;

public class MyTaskB extends TimerTask{
	@Override
	public void run() {
		System.out.println("B run timer = " + new Date());
	}
}


package chapter05.section1.thread_5_1_2.project_3_timerTestCancelMethod;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test {
	
	public static void main(String[] args) {
		System.out.println("當前時間為:"+ new Date());
		Calendar calendarRef1 = Calendar.getInstance();
		Date runDate1 = calendarRef1.getTime();
		System.out.println("計劃時間為:" + runDate1);
		MyTaskA task1 = new MyTaskA();
		MyTaskB task2 = new MyTaskB();
		Timer timer = new  Timer();
		timer.schedule(task1, runDate1,4000);
		timer.schedule(task2, runDate1,4000);
	}
}
/*
result:
當前時間為:Sat Nov 03 15:05:28 CST 2018
計劃時間為:Sat Nov 03 15:05:28 CST 2018
A run timer =Sat Nov 03 15:05:28 CST 2018
A任務自己移除自己
B run timer = Sat Nov 03 15:05:28 CST 2018
B run timer = Sat Nov 03 15:05:32 CST 2018
B run timer = Sat Nov 03 15:05:36 CST 2018
B run timer = Sat Nov 03 15:05:40 CST 2018
*/

 

5) Timer類的cancel()方法
和TimerTask類中的cancel()方法清除自身不同,Timer類中的cancel()方法作用是將任
務佇列中全部的任務進行清空。 

Terminates this timer, discarding any currently scheduled tasks. Does not 
interfere with a currently executing task (if it exists). Once a timer has 
been terminated, its execution thread terminates gracefully, and no more 
tasks may be scheduled on it.
可以看到終止的時候正在執行的任務會繼續執行,但是其他的都被清空,執行完成之後就優雅的
終止,不能再安排任務

舉例:

package chapter05.section1.thread_5_1_2.project_5_TimerCancelError;

import java.util.Date;
import java.util.TimerTask;

public class MyTaskA extends TimerTask{
	@Override
	public void run() {
		System.out.println("A run timer = " + new Date());
	}
}


package chapter05.section1.thread_5_1_2.project_5_TimerCancelError;


import java.util.Date;
import java.util.TimerTask;

public class MyTaskB extends TimerTask{
	@Override
	public void run() {
		System.out.println("B run timer = " + new Date());
	}
}


package chapter05.section1.thread_5_1_2.project_5_TimerCancelError;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test {
	
	public static void main(String[] args) throws InterruptedException{
		System.out.println("當前時間為:" + new Date());
		Calendar calendarRef1 = Calendar.getInstance();
		Date runDate1 = calendarRef1.getTime();
		System.out.println("計劃時間為:" + runDate1);
		MyTaskA task1 = new MyTaskA();
		MyTaskB task2 = new MyTaskB();
		Timer timer = new Timer();
		timer.schedule(task1, runDate1, 2000);
		timer.schedule(task2, runDate1, 2000);
		Thread.sleep(10000);
		timer.cancel();
	}	
}
/*
result:
當前時間為:Sat Nov 03 15:16:35 CST 2018
計劃時間為:Sat Nov 03 15:16:35 CST 2018
A run timer = Sat Nov 03 15:16:35 CST 2018
B run timer = Sat Nov 03 15:16:35 CST 2018
B run timer = Sat Nov 03 15:16:37 CST 2018
A run timer = Sat Nov 03 15:16:37 CST 2018
A run timer = Sat Nov 03 15:16:39 CST 2018
B run timer = Sat Nov 03 15:16:39 CST 2018
B run timer = Sat Nov 03 15:16:41 CST 2018
A run timer = Sat Nov 03 15:16:41 CST 2018
A run timer = Sat Nov 03 15:16:43 CST 2018
B run timer = Sat Nov 03 15:16:43 CST 2018
*/

 

6) Timer的cancel()方法注意事項
Timer類中的cancel()方法有時並不一定會停止計劃任務,而是正常執行

package chapter05.section1.thread_5_1_3.project_1_timerTest3;

import java.util.Date;
import java.util.TimerTask;

public class MyTaskA extends TimerTask{
	
	private int i;
	
	public MyTaskA(int i) {
		super();
		this.i = i;
	}
	
	@Override
	public void run() {
		System.out.println("第" + i + "次沒有被cancel取消");
	}
}


package chapter05.section1.thread_5_1_3.project_1_timerTest3;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test {
	
	public static void main(String[] args) {
		int i = 0;
		Calendar canlendarRef1 = Calendar.getInstance();
		Date runDate1 = canlendarRef1.getTime();
		while(true) {
			i++;
			Timer timer = new Timer();
			MyTaskA task1 = new MyTaskA(i);
			timer.schedule(task1, runDate1);
			timer.cancel();
		}
	}
}
/*
result:
.................一直在執行
*/

結果分析:

我們來看看cancel()的部分程式碼:

  public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }

下面是我們翻譯過文件中重要的一句話:

This class is thread-safe: multiple threads can share a single Timer 
object without the need for external synchronization.
這個類是執行緒安全的: 多個執行緒可以共享一個計時器物件,而不需要外部的同步。


可以看到呼叫了cancel方法,執行緒的一個欄位設定為false,不允許再次加入任務,清空隊
列,然後喚醒其他正在等待這個佇列的執行緒。並且呼叫cancel方法要獲得queue鎖,因此由
於很多個執行緒爭搶鎖,cancel()方法得不到執行,則讓TimerTask類中的任務正常執行。


(3) 方法schedule(TimerTask task, long delay)的測試
該方法的作用是以執行schedule(Timer task, long delay)方法當前的時間為參考時間,
在此時間基礎上延遲指定毫秒數後執行一次TimerTask任務

舉例:

package chapter05.section1.thread_5_1_3.project_1_timerTest3;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Run {
	static public class MyTask extends TimerTask{
		@Override 
		public void run() {
			System.out.println("運行了! 時間為: " + new Date());
		}
	}
	
	public static void main(String[] args) {
		MyTask task = new MyTask();
		Timer timer = new Timer();
		System.out.println("當前時間: " + new Date());
		timer.schedule(task, 7000);
	}
}
/*
result:
當前時間: Sat Nov 03 15:49:22 CST 2018
運行了! 時間為: Sat Nov 03 15:49:29 CST 2018
*/

任務Task被延遲了7秒執行


(4) 方法schedule(TimerTask task, long delay, long period)的測試
該方法的作用是以執行schedule(TimerTask task, long delay, long period)方法
當前的時間為參考時間,在此時間基礎上延遲指定的毫秒數,再以某一間隔時間無限次數
地執行某一任務

package chapter05.section1.thread_5_1_4.project_1_timerTest4;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Run {
	static public class MyTask extends TimerTask{
		@Override 
		public void run() {
			System.out.println("運行了! 時間為: " + new Date());
		}
	}
	
	public static void main(String[] args) {
		MyTask task = new MyTask();
		Timer timer = new Timer();
		System.out.println("當前時間: " + new Date());
		timer.schedule(task, 3000, 7000);
	}
}
/*
result:
當前時間: Sat Nov 03 15:55:07 CST 2018
運行了! 時間為: Sat Nov 03 15:55:10 CST 2018
運行了! 時間為: Sat Nov 03 15:55:17 CST 2018
運行了! 時間為: Sat Nov 03 15:55:24 CST 2018
*/

 

(5) 方法scheduleAtFixedRate(TimerTask task, Date firstTime, long period)的測試

方法schedule和方法scheduleAtFixedRate都會按順序執行,所以不要考慮非執行緒安全
的情況方法schedule和方法scheduleAtFixedRate主要的區別只在於有沒有追趕特性


1) 測試schedule方法任務不延時--Date型別

package chapter05.section1.thread_5_1_5.project_1_timerTest5;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;


public class Test1 {
	
	static public class MyTask extends TimerTask {
		@Override
		public void run() {
			try {
				System.out.println("begin timer = "+ System.currentTimeMillis());
				Thread.sleep(1000);
				System.out.println("  end timer = "+ System.currentTimeMillis());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		MyTask task = new MyTask();
		Calendar calendarRef = Calendar.getInstance();
		Date runDate1 = calendarRef.getTime();
		Timer timer = new Timer();
		timer.schedule(task,runDate1,3000);
	}
}

結果:
在不延時的情況下,如果執行任務的時間沒有被延時,則下一次執行任務的開始時間是上
一次任務的開始時間加上period時間


2) 測試schedule方法任務不延時--long型別

package chapter05.section1.thread_5_1_5.project_1_timerTest5;

import java.util.Timer;
import java.util.TimerTask;


public class Test2 {
	
	static public class MyTask extends TimerTask {
		@Override
		public void run() {
			try {
				System.out.println("begin timer = "+ System.currentTimeMillis());
				Thread.sleep(1000);
				System.out.println("  end timer = "+ System.currentTimeMillis());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		MyTask task = new MyTask();
		System.out.println("當前時間:"+System.currentTimeMillis());
		Timer timer = new Timer();
		timer.schedule(task,3000,4000);
	} 
}
/*
result:
當前時間:1541233293229
begin timer = 1541233296231
  end timer = 1541233297231
begin timer = 1541233300232
  end timer = 1541233301233
begin timer = 1541233304233
*/

 

3) 測試schedule方法任務延時--Date型別

package chapter05.section1.thread_5_1_5.project_1_timerTest5;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test3 {
	
	static public class MyTask extends TimerTask {
		@Override
		public void run() {
			try {
				System.out.println("begin timer = "+ System.currentTimeMillis());
				Thread.sleep(5000);
				System.out.println("  end timer = "+ System.currentTimeMillis());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		MyTask task = new MyTask();
		Calendar calendarRef = Calendar.getInstance();
		Date runDate1 = calendarRef.getTime();
		Timer timer = new Timer();
		timer.schedule(task,runDate1,2000);
	} 
}
/*
result:
begin timer = 1541233456659
  end timer = 1541233461660
begin timer = 1541233461660
  end timer = 1541233466660
begin timer = 1541233466660
*/

在延時情況下,如果執行任務的時間被延時,那麼下一次任務的執行時間是以上一次任務
"結束"時的時間作為參考來計算

4) 測試schedule方法任務延時--long型別

package chapter05.section1.thread_5_1_5.project_1_timerTest5;

import java.util.Timer;
import java.util.TimerTask;

public class Test4 {
	static public class MyTask extends TimerTask {
		@Override
		public void run() {
			try {
				System.out.println("begin timer = "+ System.currentTimeMillis());
				Thread.sleep(5000);
				System.out.println("  end timer = "+ System.currentTimeMillis());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		MyTask task = new MyTask();
		System.out.println("當前時間:"+System.currentTimeMillis());
		Timer timer = new Timer();
		timer.schedule(task,3000,2000);
	} 
}
/*
 result:
當前時間:1541233636669
begin timer = 1541233639671
  end timer = 1541233644671
begin timer = 1541233644671
  end timer = 1541233649672
begin timer = 1541233649672
 */

 可以看到還與上次一樣


6) 測試scheduleAtFixedRate方法任務不延時--long型別/Date型別
與schedule的不延時沒什麼區別

7) 測試scheduleAtFixedRate方法任務延時--long型別/Date型別
見原始碼,目前為止schedule和scheduleAtFixedRate還沒有什麼區別


schedule和scheduleAtFixedRate之間的區別就在於追趕性


8) 驗證schedule方法不具有追趕執行性

package chapter05.section1.thread_5_1_5.project_1_timerTest5;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test9 {

	static public class MyTask extends TimerTask {
		@Override
		public void run() {
			System.out.println("begin timer = " + new Date());
			System.out.println("  end timer = " + new Date());
		}
	}

	public static void main(String[] args) {
		MyTask task = new MyTask();
		System.out.println("現在執行時間:" + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.set(Calendar.SECOND, calendarRef.get(Calendar.SECOND) - 20);
		Date runDate = calendarRef.getTime();
		System.out.println("計劃執行時間:" + runDate);
		Timer timer = new Timer();
		timer.schedule(task, runDate, 2000);
	}
}
/*
result:
現在執行時間:Sat Nov 03 16:35:34 CST 2018
計劃執行時間:Sat Nov 03 16:35:14 CST 2018
begin timer = Sat Nov 03 16:35:34 CST 2018
  end timer = Sat Nov 03 16:35:34 CST 2018
begin timer = Sat Nov 03 16:35:36 CST 2018
  end timer = Sat Nov 03 16:35:36 CST 2018
begin timer = Sat Nov 03 16:35:38 CST 2018
  end timer = Sat Nov 03 16:35:38 CST 2018
begin timer = Sat Nov 03 16:35:40 CST 2018
*/

可以看到早於34秒的任務被取消,不被執行了,這就是Task任務的不追趕


10) 驗證scheduleAtFixedRate方法具有追趕執行性

package chapter05.section1.thread_5_1_5.project_1_timerTest5;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test10 {
	
	static public class MyTask extends TimerTask {
		@Override
		public void run() {
			System.out.println("begin timer = " + new Date());
			System.out.println("  end timer = " + new Date());
		}
	}

	public static void main(String[] args) {
		MyTask task = new MyTask();
		System.out.println("現在執行時間:" + new Date());
		Calendar calendarRef = Calendar.getInstance();
		calendarRef.set(Calendar.SECOND, calendarRef.get(Calendar.SECOND) - 20);
		Date runDate = calendarRef.getTime();
		System.out.println("計劃執行時間:" + runDate);
		Timer timer = new Timer();
		timer.scheduleAtFixedRate(task, runDate, 2000);
	}
}
/*
result:
現在執行時間:Sat Nov 03 16:38:45 CST 2018
計劃執行時間:Sat Nov 03 16:38:25 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:45 CST 2018
  end timer = Sat Nov 03 16:38:45 CST 2018
begin timer = Sat Nov 03 16:38:47 CST 2018
  end timer = Sat Nov 03 16:38:47 CST 2018
begin timer = Sat Nov 03 16:38:49 CST 2018
  end timer = Sat Nov 03 16:38:49 CST 2018
begin timer = Sat Nov 03 16:38:51 CST 2018
*/

可以看到將兩個時間段內的時間多對應的Task任務被"補充性"地執行,這就是Task任務
追趕特性。
但是我們看到補充的數量不對。


總結: 這些程式碼可以應用在Android技術中,實現類似於輪詢、動畫等常見的主要功能。