JAvaのscheduledThreadPool

6751 ワード

詳細
今日jdkのタイミングタスクのソースコードを見て、この記録をもう一度私の気持ちを確認して、問題が発生したときの検索を便利にしました.
今回突発的にタイミングタスクの原理を見たいと思ったのは、コードに使われていて、定期的にタスクを実行すると思っていたのですが、スレッド+while trueの操作は使いたくないので、jdkの機能を思い出しましたが、彼の原理を知っていて、分岐を恐れていたので見てみました.このブログを見るには、スレッドプールの原理、すなわちExecutors.newFixedThreadPoolまたはExecutors.newCachedThreadPoolの実現原理を知る必要があります.(実は簡単です.1つのブロックキューを使用して、複数のスレッドを起動してキューからタスクを取得します.キューにタスクがない場合は、スレッドをブロックします.スレッドの起動数は2つのパラメータによって制御されます.簡単です).
Executors.newSingleThreadScheduledExecutorを使用しましたが、この方法もあります.Executors.newScheduledThreadPoolです.両者の違いは前者は1つのスレッドしか起動できませんが、後者は複数(自分で数を作ることができます)を起動できます.方法の中で最終的には呼び出しです
 public ScheduledThreadPoolExecutor(int corePoolSize) {//              ,
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,//         ,   newFixedThreadPool     ,                      corePoolSize     。
              new DelayedWorkQueue());
    }

 ここでsuperはThreadPoolExecutorであり、タイミングスケジューリングのスレッドプールと非固定スケジューリングのスレッドプールはいずれもThreadPoolExecutorから継承されているが、入力された2番目のパラメータは機能しない(fixexThreadPoolまたはCacheThreadPoolで機能する)、そして最も重要なのは最後のブロックキューであり、FixedThreadPool(またはCachedThreadPool)で、どちらも使用しているLinkedBlockingQueueですが、ここに伝わるのはDelayedWordQueueですので、このクラスをご紹介しましょう.
このクラスもブロックキューで、BlockingQueueのインタフェースを実現しましたが、彼が使っているコンテナはスタックです(配列で実現されています)前のLinkedBlockingQueueのようにチェーンテーブルを使って実現するのではなく、それまでのLinkedBlockingQueueのようにチェーンテーブルを使って実現していました.彼の原理は簡単で、読み込み時にスタックに何もない場合はlock conditionを使ってブロックし、ある場合はlockロックを使ってロックし、キューから読み込み、ロックを解除します.追加する時もlockを使ってロックし、追加が完了すると目が覚める前に眠りますのスレッド(もしあれば)、そのスタックについては、必ずソートの論理を制定しなければならないので、彼のofferメソッド(つまりキューの追加メソッド)を見てみましょう.彼は参加したすべてのRunnableをRunnableScheduledFutureに変換します(このクラスの使用には大きな制限があるようです).ああ、このRunnableScheduledFutureクラスを見てみましょう.彼はFutureでもあり、Runnableでもあり、Delayでもあります.Delayは初めて出会ったのです.彼は簡単です.実はgetDelayメソッドを呼び出すことができるものです.DelayedWordQueueでは、このDelayのgetDelayメソッドに基づいて、加入したRunnableを記憶的にソートし、delayが小さく前に並んでいます.彼の本当の意味は定期的に実行するタスク(つまりRunnableScheduledFuture)をソートすると、各スレッドがキューからタスクを取得するときにスタックトップのタスクを取得し、開始に最も近いタスクが限られて呼び出されることが保証されます.
 
DelayedWordQueue、つまり最終的に使用された容器を見終わったので、本当のスケジュールを見てみましょう.ScheduledThreadPoolExecutorというscheduleAtFixedRateを見てみましょう.
    public ScheduledFuture> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask sft =
            new ScheduledFutureTask(command,null,triggerTime(initialDelay, unit),unit.toNanos(period));// ScheduledFutureTask  runable,
        RunnableScheduledFuture t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);// scheduledFuture           ,  ,    ,           ,  run     ,      ScheduledFutureTask run   
        return t;
}

 
もう一度scheduleWithFixedDelayを見てみると、彼の操作はほとんど上のfixedRateと同じであることがわかります.
public ScheduledFuture> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (delay <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask sft =
            new ScheduledFutureTask(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(-delay));//          delay     
        RunnableScheduledFuture t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

 最後のスレッドがキューから得たタスクはScheduledFutureTaskであることを覚えています.彼のrunメソッドを見てみましょう.
public void run() {
            boolean periodic = isPeriodic();//        periorid   0,     schedule     ,     0,fixedRate  fixDelay  0
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)
                ScheduledFutureTask.super.run();//     ,     schedule  ,      
            else if (ScheduledFutureTask.super.runAndReset()) {//      , period  0,    super runAndReset  ,   run  ,      true,      ,    !
                setNextRunTime();//         (           )
                reExecutePeriodic(outerTask);//    
            }
        }

 まず、ScheduledFutureTask.super.runAndReset()メソッドを見てみましょう.つまり、いつ定期的に実行されますか.
 protected boolean runAndReset() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;
        boolean ran = false;
        int s = state;
        try {
            Callable c = callable;
            if (c != null && s == NEW) {
                try {
                    c.call(); // don't set result,   ,     ,  ran=false
                    ran = true;
                } catch (Throwable ex) {
                    setException(ex);
                }
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        return ran && s == NEW;//  ran=false,    false,       
    }

 上記のコードから、タスクを実行するときにエラーが報告された場合、このタスクは繰り返し実行されないことがわかります.
setNextRuntTimeをもう一度見てみましょう
private void setNextRunTime() {
      long p = period;
      if (p > 0)//   fixedRate,            ,      ,
         time += p;
      else
         time = triggerTime(-p);//   fixedDelay       (    )+ delay
}

 以上の方法から、fixedDelayは、タスク実行後の現時点でdelay時間が経過してからタスクを実行していると判断できます.fixedRateであれば、前回のタスクの実行すべき時間+delayを次の実行時間として、この時間は現在の絶対時刻より前になる可能性があります!
over!