在多線程技術中,用的較多的就是Timer計時器了,它本身不是Runnable的實作類,計劃任務用另一個TimerTask類來實作。
應用場景:比如在報表統計中常常需要使用任務排程來更新報表庫 。
Timer.schedule(TimerTask,Date)
我們用Timer的schedule方法來設定一個任務,Date為任務的執行時間。Timer.schedule(TimerTask,long),當第二個參數是long類型的時候,代表延遲自執行時間,當存在第三個參數的時候,代表周期。比如Timer.schedule(TimerTask,long,long)
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務執行了-" + new Date());
}
};
String timeStr = "2018-7-22 17:29:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time = sdf.parse(timeStr);
timer.schedule(task, time);
}
}
輸出結果:
任務執行了-Sun Jul 22 18:09:01 CST 2018
發現任務立即執行,這也就說明了如果任務設定時間早于目前時間,就立即執行任務。
我們再來看任務時間晚于目前時間的情況:
這裡寫代碼片public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務執行了-" + new Date());
}
};
String timeStr = "2018-7-22 18:13:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time = sdf.parse(timeStr);
timer.schedule(task, time);
}
}
輸出結果:
任務執行了-Sun Jul 22 18:13:00 CST 2018
一個Timer設定多個Task
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務1執行了-" + new Date());
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("任務2執行了-" + new Date());
}
};
String timeStr = "2018-7-22 18:19:00";
String timeStr2 = "2018-7-22 18:20:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time = sdf.parse(timeStr);
Date time2 = sdf.parse(timeStr2);
timer.schedule(task, time);
timer.schedule(task2, time2);
}
}
輸出結果:
任務1執行了-Sun Jul 22 18:19:00 CST 2018
任務2執行了-Sun Jul 22 18:20:00 CST 2018
Timer定時器不會自動結束
當new Timer()之後,發現程序不會自動結束。因為Timer不是一個守護線程,main線程結束之後,它不會自動結束。
來看一下Timer的構造函數:
public Timer(String name) { thread.setName(name); thread.start(); }
方法一:設定為守護線程
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
Timer timer = new Timer(true);
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務1執行了-" + new Date());
}
};
System.out.println("現在時間-"+ new Date());
String timeStr = "2018-7-22 19:16:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time = sdf.parse(timeStr);
timer.schedule(task, time);
}
}
輸出結果:
現在時間-Sun Jul 22 19:15:36 CST 2018
然後就直接結束了
發現如果設定成守護線程,那麼主線程結束了,該任務也就得不到執行了。
方法二:停止等待
使用該方法,可以讓主程式等待Task任務執行完成之後才執行後面的語句。這就讓Timer起到了一個計時器的效果。
import java.text.ParseException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class T{
private static boolean flag = true;
public static void main(String[] args) throws InterruptedException, ParseException {
Timer timer = new Timer();
final ReentrantLock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務1執行了-" + System.currentTimeMillis());
try{
lock.lock();
}finally{
condition.signal();
flag = false;
lock.unlock();
}
}
};
System.out.println("現在時間-"+ System.currentTimeMillis());
timer.schedule(task, ); //設定到2000ms後執行該任務
try{
lock.lock();
while(flag){
condition.await();
}
}finally{
lock.unlock();
}
timer.cancel();
System.out.println("任務執行完了,執行後面的語句");
}
}
以上的做法,完全可以在一個線程中完成,也就是說,我們完全可以讓main線程sleep(2000)再來執行後面的語句。
方法三:直接釋放Timer(Timer.cancle)
完全可以在task中調用timer的cancle()方法實作停止線程。
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
final Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務執行-" + System.currentTimeMillis());
timer.cancel();
}
};
timer.schedule(task, );
System.out.println("main語句-"+System.currentTimeMillis());
}
}
顯然這種做法更加合理。但是要值得注意的是,Timer.cancle()會清楚計時器内所有的task,并且停止timer中的線程等待垃圾回收器回收。
TimerTask.cancle()
清空目前任務,取消後續的周期執行。如果目前任務是周期性的,當調用TimerTask.cancle()方法後,它将得不到後續的周期性執行。
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
final Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務A執行-" + System.currentTimeMillis());
this.cancel(); //下個周期不會執行
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("任務B執行-" + System.currentTimeMillis());
}
};
//2000ms後執行,并且以1000ms為周期繼續執行
timer.schedule(task, ,);
timer.schedule(task2, ,);
}
}
輸出結果:
任務A執行-1532269174240
任務B執行-1532269174240
任務B執行-1532269175240
任務B執行-1532269176241
任務B執行-1532269177241
周期時間小于任務執行時間
當周期時間小于任務執行時間的時候,schedule()的周期改成任務執行的時間為周期。例如,任務執行時間為3000ms,周期為2000ms,那麼周期将會變成3000ms。
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
final Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務A執行-" + new Date());
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//2000ms後執行,并且以2000ms為周期繼續執行
timer.schedule(task, ,);
}
}
輸出結果:
任務A執行-Mon Jul 23 00:24:19 CST 2018
任務A執行-Mon Jul 23 00:24:22 CST 2018
任務A執行-Mon Jul 23 00:24:25 CST 2018
任務A執行-Mon Jul 23 00:24:28 CST 2018
Timer.schedule()和Timer.scheduleAtFixedRate()
值得注意的是,如果計劃的時間設定在過去,那麼就會立即執行。Timer.schedule()和Timer.scheduleAtFixedRate()的都是設定計劃,那麼它們的差別在哪呢?
public class T{
public static void main(String[] args) throws InterruptedException, ParseException {
final Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任務A執行-" + new Date());
}
};
TimerTask task2 = new TimerTask(){
@Override
public void run() {
System.out.println("任務B執行-" + new Date());
}
};
//2000ms後執行,并且以1000ms為周期繼續執行
System.out.println(new Date());
String date = "2018-07-23 14:06:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time = sdf.parse(date);
timer.schedule(task, time, **);
timer.scheduleAtFixedRate(task2, time, **);
}
}
輸出結果:
Mon Jul 23 14:07:21 CST 2018
任務A執行-Mon Jul 23 14:07:21 CST 2018
任務B執行-Mon Jul 23 14:07:21 CST 2018
任務B執行-Mon Jul 23 14:09:00 CST 2018
任務A執行-Mon Jul 23 14:10:21 CST 2018
任務B執行-Mon Jul 23 14:12:00 CST 2018
任務A執行-Mon Jul 23 14:13:21 CST 2018
-
輸出結果分析:
我們設定的時間是 06分,但是現在的時間是07分,但是我們發現09的時候B計劃執行了,也就是說Timer.scheduleAtFixedRate()設定的3分鐘周期是從06分開始的,而Timer.schedule()設定的3分鐘周期是從07分開始的。
-
差別:
scheduleAtFixedRate()是從計劃時間開始計算,schedule()是從第一次執行器開始計算。
共同點是,如果任務時間設定到過去,那麼都會立即執行。
這裡還應該注意一點,如果過去時間過去太久,已經經過了好幾個周期,scheduleAtFixedRate()會追趕彌補,schedule()不會追趕彌補
-
應用場景
A的資料量極大,B庫的資料來自于A庫,我們提取A庫中的資料進行統計制作報表,如果每次檢視報表都去A庫中提取,效率很低,這時候需要定時從A庫中周期的提取資料到B庫,這時候可以用到該計時器設定TimerTask定時執行任務。