1. 程式人生 > >quartz詳解4:quartz執行緒管理

quartz詳解4:quartz執行緒管理

http://blog.itpub.net/11627468/viewspace-1766967/

quartz啟動後有多個執行緒同時在跑。啟動時會啟動主執行緒、叢集執行緒、檢漏執行緒、工作執行緒。主執行緒負責查詢到需要觸發的執行緒,並放入到執行緒佇列。
叢集執行緒負責叢集、檢漏執行緒負責對未成功執行的任務進行檢漏。工作執行緒預設是20,一般PC伺服器可以調到200。


一、主執行緒QuartzScheduleThread
關於QuartzScheduleThread是quartz啟動時開始啟動,用於trigger的獲取、觸發,並放入到執行緒池中執行。
詳細可以看第4章


詳細流程如下:

關於quartz2.0版本之後可以批量執行trigger的功能。要測試是否影響定時的準確度。


二、執行緒池SimpleThreadPool


執行緒池的初使化:new 執行緒並放入執行緒池的連結串列中。
執行執行緒:把任務放到一個執行緒中執行。
執行緒結束:修改執行緒狀態。
執行緒池關閉:每個執行緒關閉,正在執行的執行緒等執行緒執行完了再關閉。



、工作執行緒WorkThread
執行緒池中執行的工作執行緒,可以通過配置檔案quartz.properties來配置大小:

JobRunShell實現runnable介面,放入到workThread下執行:


四、叢集執行緒ClusterManager和檢漏執行緒MisfireHandler
可以看到quartz啟動時,這兩個執行緒也啟動了。

那麼,它是在什麼時候啟動的呢?是在執行QuartzScheduler的start()方法時在JobStore類上載入的。


再來分析下,這兩個執行緒的作用。

這兩個執行緒都有initialize,manage,run,shutdown方法。
--1、先看ClusterManager,在new ClusterManger後,就會觸發initialize方法,initialize方法再呼叫manager方法。
run方法的程式碼:

點選(此處)摺疊或開啟

  1. public void run() {
  2.             while (!shutdown) {
  3.                 if (!shutdown) {
  4.                     long timeToSleep = getClusterCheckinInterval(
    );
  5.                     long transpiredTime = (System.currentTimeMillis() - lastCheckin);
  6.                     timeToSleep = timeToSleep - transpiredTime;
  7.                     if (timeToSleep <= 0) {
  8.                         timeToSleep = 100L;
  9.                     }
  10.                     if(numFails > 0) {
  11.                         timeToSleep = Math.max(getDbRetryInterval(), timeToSleep);
  12.                     }
  13.                     try {
  14.                         Thread.sleep(timeToSleep);
  15.                     } catch (Exception ignore) {
  16.                     }
  17.                 }
  18.                 if (!shutdown && this.manage()) {
  19.                     signalSchedulingChangeImmediately(0L);
  20.                 }
  21.             }//while !shutdown
  22.         }
private long clusterCheckinInterval = 7500L;   //預設7.5秒執行一次叢集的manage。
而叢集的manager做的事情是判斷是否有節點down掉,同時每7.5秒傳送同步心跳修改資料庫資訊。

LAST_CHECKIN_TIME每7.5秒會更新一次。

--2、再看MisfireHandler的作用:
run方法預設每15秒執行一次。如果沒有misfire的話,則每60秒執行一次。
manager邏輯如下:
 private long misfireThreshold = 60000L;   //預設時間超過了1分鐘。
就是把超過1分鐘還沒執行的任務,認為是misfire,然後儲存到trigger表,等待重新再執行。

有狀態的job如果第一次沒有執行完,第二次執行的時間錯過了,就會被認為是misfire
doUpdateOfMisfiredTrigger呼叫CronTriggerImpl的updateAfterMisfire方法。
如果misfire設定為MISFIRE_INSTRUCTION_SMART_POLICY或MISFIRE_INSTRUCTION_FIRE_ONCE_NOW就是馬上執行。
如果misfire設定為MISFIRE_INSTRUCTION_DO_NOTHING則在下一個週期再執行。
預設是:MISFIRE_INSTRUCTION_SMART_POLICY
可以通過trigger的build方法增加引數實現:
withMisfireHandlingInstructionFireAndProceed().build()
withMisfireHandlingInstructionDoNothing().build()

五、總結:
從執行緒角度來分析叢集的效能的話,主要是:
1、主執行緒QuartzScheduleThread的瓶頸很可能出現在資料庫行鎖。
--可以考慮定時任務資料儲存在分散式快取。減少對資料庫的過分依賴。
2、工作執行緒WorkThread的瓶頸很可能出現在任務的阻塞。
--可以通過用非同步任務來解析,非同步任務如何放在資料庫有效能問題可以再考慮分散式快取。