天天看點

java實作周期性執行(定時任務)Timer和TimerTask的使用 :

        最近是遇到一個裝置線上離線的判定問題,設計是每個多長時間(正常的定時任務)檢測一次裝置是否在前,當檢測到裡離線時,我們不能立馬判斷為離線,而是要在重試多測幾次,隻要一次成功就傳回判定為線上,多次都不成功側是離線,我這裡相當了用ScheduledThreadPoolExecutor來實作,如有不足還請提出。如下:

ScheduledThreadPoolExecutor的介紹:

         ScheduledThreadPoolExecutor,它可另行安排在給定的延遲後運作指令,或者定期執行指令。需要多個輔助線程時,或者要求 ThreadPoolExecutor 具有額外的靈活性或功能時,此類要優于Timer。

ScheduledThreadPoolExecutor的使用詳解

        當程式需要用到一個定時器處理問題的時候,并且需要處理的頻率是很快的,這就需要一個穩定的定時器來保證資料的長久進行。ScheduledThreadPoolExecutor這個類就是個很好的選擇。正常情況下,定時器我們都是用Timer和TimerTask這兩個類就能完成定時任務,并且設定延長時間和循環時間間隔。

ScheduledThreadPoolExecutor也能完成Timer一樣的定時任務,并且時間間隔更加準确。

誤差說明:

       我在背景程式看看一下Timer執行程式是有可能延遲1、2毫秒,如果是1秒執行一次的任務,1分鐘有可能延遲60毫秒,一小時延遲3600毫秒,相當于3秒,實際使用者看不出什麼差別。 但是,如果我的程式需要每40毫秒就執行一次任務,如果還是有1、2毫秒的誤差,1秒鐘就有25毫秒的誤差,大概40秒就有1秒的誤差,十幾分鐘就有十幾秒的誤差,這對UI顯示來說是延遲非常嚴重的了。 而我用ScheduledThreadPoolExecutor來做40毫秒的間隔任務,一般十幾分鐘才有1秒多的誤差,這個還是能接受的。 這也是我用ScheduledThreadPoolExecutor這個類的原因。

使用Timer和TimerTask存在一些缺陷:

1.Timer隻建立了一個線程。當你的任務執行的時間超過設定的延時時間将會産生一些問題。

2.Timer建立的線程沒有處理異常,是以一旦抛出非受檢異常,該線程會立即終止。

JDK 5.0以後推薦使用ScheduledThreadPoolExecutor。該類屬于Executor Framework,它除了能處理異常外,還可以建立多個線程解決上面的問題

Timer和TimerTask的使用 :

這裡就不做過多的描述了,重點在ScheduledThreadPoolExecutor。

Timer  timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.e("time:");

            }
        }, 2000, 40);
//2000表示第一次執行任務延遲時間,40表示以後每隔多長時間執行一次run裡面的任務
           

ScheduledThreadPoolExecutor的使用:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 裝置線上延時檢查檢查
 */
@Slf4j
public class DelayedCheckDeviceSchedule {

  public static final Integer CONNECT_TIME_OUT = 10000;

//引用的業務層
  private ITblBaseService baseService = SpringContextHolder.getBean(ITblBaseService .class);

  private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = null;

  /**
   * 需要延時檢查的裝置狀态的裝置id集合
   */
  public static Set<String> deviceSet = new HashSet<>();

  /**
   * 目前執行點
   */
  private AtomicInteger currentAtomicInteger = new AtomicInteger(1);


  /**
   * 初始化任務
   * @param delay 延遲幾秒執行
   * @param checkCount 需要檢測的次數
   * @param deviceId 裝置Id
   * @param deviceType 裝置類型
   * @return
   */
  public boolean init(long delay, int checkCount, String deviceId, String deviceType) {
    log.info("第一次初始化時間"+deviceId+":"+System.currentTimeMillis());
    if (deviceSet.add(deviceId) && deviceConnectModel!=null) {
      this.scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(4);
      this.scheduledThreadPoolExecutor.schedule(new Runnable() {
        @Override
        public void run() {
          executor(delay, checkCount, deviceId, deviceType);
        }
      }, delay, TimeUnit.SECONDS);
      return true;
    }
    return false;
  }

  /**
   * 執行體
   */
  private void executor(long delay, int checkCount, String deviceId, String deviceType) {
    log.info("第"+currentAtomicInteger.get()+"執行時間"+deviceId+":"+System.currentTimeMillis());
    if (deviceSet.contains(deviceId) && currentAtomicInteger.get() < (checkCount+1)) {
      //執行邏輯
          
       //當滿足條件時,停止任務
        if(currentAtomicInteger.get()==checkCount){ 
          //需要處理的邏輯

          //停止任務
          this.scheduledThreadPoolExecutor.shutdownNow();
          deviceSet.remove(deviceId);
        }else {
          //下一次執行
          currentAtomicInteger.getAndIncrement();
          this.scheduledThreadPoolExecutor.schedule(new Runnable() {
            @Override
            public void run() {
              executor(delay, checkCount, deviceId, deviceType);
            }
          }, delay, TimeUnit.SECONDS);
     
       }
    } else {
      this.scheduledThreadPoolExecutor.shutdownNow();
      this.scheduledThreadPoolExecutor = null;
    }
  }

  /**
   * 停止檢測任務
   * @param deviceId
   * @return
   */
  public static boolean stop(String deviceId) {
    return deviceSet.remove(deviceId);
  }

}

           

在需要周期性檢查的時候引入:

DelayedCheckDeviceSchedule delayedCheckDeviceSchedule = new DelayedCheckDeviceSchedule();
              delayedCheckDeviceSchedule.init(10, 3, panModel.getDeviceId(), "pan");