Spring Quartzタイミングタスクが時間通りに実行されない


1.はじめに
    quartzタイミングタスクが予想された時間に実行されなかったという問題も、この間の作業で発生しました.その後quartzのメカニズムを研究し,原因を明らかにし,ここで記録と共有を行った.
 
2.原因の説明
    まずspring quartzの大まかなメカニズムや原理を見てみましょう.quartzタスクは、1つのプライマリスレッドとスレッドプール内の複数の具体的なワークスレッドから構成されます.
    メインスレッドはQuartzSchedulerThreadで、主に特定のタイミングタスクとそのタスクが実行される時間(例えばcron expressionで時間を得ることができる)を取得し、スレッドプールにタスクを配布します.
    具体的なタスクはスレッドプール内の作業スレッドによって実行され、デフォルトのスレッドプールクラスはSimpleThreadPoolであり、作業スレッドはその内部クラスWorkerThreadであり、デフォルトのスレッド数は10.WorkerThreadは具体的な作業タスクを受け取り、実行する.
 
    10個のWorkerThreadがすべてタスクの処理中であると仮定し(それぞれのタスクがまだ処理されていない)、すべてのタスクがステータスなしであると仮定します(stateless).このとき11番目のタスクが来ると、このタスクを処理できる暇なWorkerThreadはありません.これにより、タスクの実行が予想されると、タスクは実行されず、タスクが遅延する現象が発生します.11番目のタスクは、あるWorkerThreadがタスクを処理したときに、そのWorkerThreadによって受け取り処理されます.
 
   当時、作業で発生した問題は、いくつかのタスクが長い時間をかけて処理され、作業スレッドはすべて「忙しい」状態にあり、新しいタスクをタイムリーに処理するために空きスレッドがありませんでした.
  * デフォルトのスレッドプールクラスでは、作業スレッド数はquartz jarパッケージのquartz.propertiesファイルに設定されます.
 
3.コードテストと検証
package test;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
 
public class TestCronJob {
 
 public static void main(String[] args){
  SchedulerFactory sf = new StdSchedulerFactory();
  try {
   // below including instantiate QuartzScheduler,
   // where quartz QuartzSchedulerThread is instantiated.
   Scheduler sched = sf.getScheduler(); 
      sched.start();
      JobDetail jd = new JobDetail("myjob",sched.DEFAULT_GROUP,MyJob.class);
      System.out.println("stateful:"+jd.isStateful());
      CronTrigger ct = new CronTrigger("JobName","DEFAULT","*/10 * * * * ? *");
      sched.scheduleJob(jd, ct); 
  }catch(Exception e){
   e.printStackTrace();
  }
 }
}


package test;
import java.sql.Timestamp;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.StatefulJob;
public class MyJob implements Job{
 
 public void execute(JobExecutionContext arg0) throws JobExecutionException{ 
  System.out.println(new Timestamp(System.currentTimeMillis())+",job executed [" + Thread.currentThread().getName()+"]");
  try {
   Thread.sleep(2*60*1000);
  }catch(InterruptedException e){
   e.printStackTrace();
  }
  System.out.println(new Timestamp(System.currentTimeMillis())+",job executed ["+Thread.currentThread().getName()+"]");
 }
}

 
上記のコードの効果は次のとおりです.
 10秒ごとにMyJobのタスクを実行し、MyJobはいくつかの情報を印刷します.注意このタスクを実行するのに2分かかります(スリープのため).
 最終出力によれば,最初の10個のタスクは時間通りに実行され,互いに10 s間隔を置いていることがわかる.11番目のタスクは30 s遅延し、あるタスクが実行されるまで11番目のタスクが処理されません.