Java定時器Timer的使用


  定時計划任務功能在Java中主要使用的就是Timer對象,它在內部使用多線程的方式進行處理,所以它和多線程技術還是有非常大的關聯的。

  1、看下面代碼:

 1     public void TestUserNvrConnectOnline() {
 2         Timer timer = new Timer(true);
 3         timer.schedule(new TimerTask() {
 4             public void run() {
 5                 //你要定時執行的功能
 6                 isUserNvrConnectLast();
 7             }
 8         }, 0, 60 * 1000);
 9 
10     }
View Code

  周期執行任務,Timer.schedule(TimerTask task,long firstTime,long period),參數task是要執行代碼的主體,參數firstTime是Timer在系統啟動后第一次運行的時間毫秒值,參數period是代碼循環運行的時間間隔,每次最少等待period執行一次。

  2、Timer還有其他幾種使用方式,基本上大同小異,比如:

    
    private Set<Integer> unitTimeSet = new HashSet<>(24);//每天更新整點數,把已經檢查的時間記錄下來
 1     public void checkDeviceDeopped() {
 2         Calendar calendar = Calendar.getInstance();
 3         //此時要在 第一次執行定時任務的時間加一小時,以便此任務在下個時間點執行。
 4         calendar.add(Calendar.HOUR_OF_DAY,1);
 5         calendar.set(Calendar.MINUTE, 0);
 6         calendar.set(Calendar.SECOND, 0);
 7         java.util.Date date = calendar.getTime(); //第一次執行定時任務的時間
 8 
 9         Timer timer = new Timer(true);
10         timer.schedule(new TimerTask() {
11             public void run() {
12                 Calendar cal = Calendar.getInstance();
13                 int hour = cal.get(Calendar.HOUR_OF_DAY);
14 
15                 if (hour == 0){
16                     unitTimeSet.clear();
17                 }
18                 if (hour % unitTime == 0 && !unitTimeSet.contains(hour)){
19                     Session session = sessionFactory.openSession();
20                     cal.set(Calendar.MINUTE, 0);
21                     cal.set(Calendar.SECOND, 0);
22                     java.util.Date endTime = cal.getTime();
23                     cal.add(Calendar.HOUR_OF_DAY,-1);
24                     java.util.Date startTime = cal.getTime();
25                     for (String deviceId : mapNvr.keySet()) {
26                         //查詢設備在指定時間內的掉線次數
27                         Integer offLineNum = 0;
28                         try {
29                             offLineNum = (Integer) session.createQuery("SELECT COUNT(*) FROM SystemLog WHERE workerid= :deviceId AND dt<= :endTime AND dt>= :startTime ").setParameter("deviceId", deviceId)
30                                     .setParameter("endTime", endTime, TemporalType.TIMESTAMP).setParameter("startTime", startTime, TemporalType.TIMESTAMP).uniqueResult();
31                         }catch (Exception e){
32                             e.printStackTrace();
33                         }
34 
35                         if (offLineNum >= 10){
36                             //發送郵件
37                             try {
38                                 CompletableFuture.runAsync(() -> {
39                                     sendMail("NUM", deviceId);
40                                 });
41                             }  catch (Exception e) {
42                                 e.printStackTrace();
43                             }
44                         }
45                     }
46                 }
47                 unitTimeSet.add(hour);
48             }
49         }, date,60 * 60 * 1000); 
50 }

  上面的代碼是在每天的整點執行一次定時任務,查看設備離線次數,如果超過10次就發送郵件,循環時長和離線次數可另外定義變量進行控制,發送郵件調用了異步,感興趣的朋友可以查看我的另外文章Java異步CompletableFuture的使用

  3、另外形式的定時器不再上代碼,大體如下:

    1)、public void schedule(TimerTask task, long delay)

      經過時長delay后執行,僅執行一次。

    2)、public void schedule(TimerTask task, Date time)

      在指定的時間點time執行,僅執行一次。

    3)、public void scheduleAtFixedRate(TimerTask task, long delay, long period)

      調用一個task,第一次在delay時長后執行,以后每次經過period執行一次,看起來和schedule這種方式一樣,實際是有區別的。schedule在計算下一次執行的時間的時候,是通過當前執行的時間(在任務執行前得到) + 時間片period,而scheduleAtFixedRate方法是通過當前需要執行的時間(計算出現在應該執行的時間)+ 時間片,前者是程序運行的實際時間,是動態的,而后者是指定的理論時間點,代碼編譯后就是固定不變的。我們可以看一下源碼部分,schedule(TimerTask task, long delay,long period)的源碼如下:

    public void schedule(TimerTask task, long delay, long period) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, System.currentTimeMillis()+delay, -period);
    }

再看scheduleAtFixedRate(TimerTask task, long delay, long period)的源碼如下:

1     public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
2        if (delay < 0)
3            throw new IllegalArgumentException("Negative delay.");
4        if (period <= 0)
5            throw new IllegalArgumentException("Non-positive period.");
6        sched(task, System.currentTimeMillis()+delay, period);
7    }

看起來沒有區別,唯一的區別就是sched(task, System.currentTimeMillis()+delay, period)的第三個參數,schedule加了一個負號,其實就是在調用sched方法時用來區分schedule和scheduleAtFixedRate的。我們看sched的代碼如下:

 1     private void sched(TimerTask task, long time, long period) {
 2         if (time < 0)
 3             throw new IllegalArgumentException("Illegal execution time.");
 4 
 5         synchronized(queue) {
 6             if (!thread.newTasksMayBeScheduled)
 7                 throw new IllegalStateException("Timer already cancelled.");
 8 
 9             synchronized(task.lock) {
10                 if (task.state != TimerTask.VIRGIN)
11                     throw new IllegalStateException(
12                         "Task already scheduled or cancelled");
13                 task.nextExecutionTime = time;
14                 task.period = period;
15                 task.state = TimerTask.SCHEDULED;
16             }
17 
18             queue.add(task);
19             if (queue.getMin() == task)
20                 queue.notify();
21         }
22     }

queue是一個隊列,他在做這個操作的時候,發生了同步,所以在timer級別,這個是線程安全的,最后將task相關的參數賦值,主要包含nextExecutionTime(下一次執行時間),period(時間片),state(狀態),然后將它放入queue隊列中,做一次notify操作;我們看一下queue的結構:TaskQueue

1     class TaskQueue {
2 
3     private TimerTask[] queue = new TimerTask[128];
4 
5     private int size = 0;

可以看到,TaskQueue的結構是一個數組,加一個size,有點像ArrayList,ArrayList可以擴容,TaskQueue也可以,只是會造成內存拷貝,所以對於一個Timer來講,只要內部的task個數不超過128是不會擴容的;內部 提供了add(TimerTask)、size()、getMin()、get(int)、removeMin()、quickRemove(int)、 rescheduleMin(long newTime)、isEmpty()、clear()、fixUp()、fixDown()、heapify()等,這里不再詳述,有興趣的朋友可以查閱相關資料。

 

    4)、public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)

      和3)的執行時間一樣

  需要注意,TimerTask 是以隊列的方式一個一個被順序運行的,所以執行的時間和預期的時間可能不一致,因為前面的任務可能消耗的時間較長,則后面的任務運行的時間會被延遲。延遲的任務具體開始的時間,就是依據前面任務的"結束時間"。Timer仍有很多需要注意的地方,也有一些其他的用法,不再贅述,有興趣的伙伴可以繼續查閱。

  參考來源:https://www.cnblogs.com/0201zcr/p/4703061.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM