タイマーTimer


タイマーTimer
どうしてタイマーを使いますか?
例えばwebアプリケーションは、このアプリケーションの規模が大きいなら、ログデータが多いですか?サーバーの記憶量をずっと残しておくと、おそらくダメでしょう。しばらく置いて削除する必要があります。スレッドを一つ置きにログデータを削除する必要があります。
直接プログラムを実行:
import java.util.Timer;
import java.util.TimerTask;

public class TestTimer {
public static void main(String[] args) {
    Timer timer=new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("    "+"  "+System.currentTimeMillis());
        }
    },1000,2000);
}
}
実行結果:
Timerはスケジューラであり、TimerTaskはスケジューラである。
Timer類
作成方法
Timer()は新しいタイマーを作成します。
Timer(bolean isDaemen)は新しいタイマーを作成し、その関連スレッドはrun as a daemenとして指定できます。
Timer(String name)は、指定された名前を持つ関連スレッドを作成する新しいタイマーを作成します。
Timerは新しいタイマーを作成しました。その関連スレッドは指定された名前を持ち、run as a daemenと指定できます。
デーモンに指定するとどんな効果がありますか?
//      new Timer      true
Timer timer=new Timer(true);
デーモンプロセスですので、mainスレッドは終了しますので、直接終了することはありません。
方法
void cancel()はこのタイマを終了し、現在の計画のタスクを廃棄し、タスクキューのすべてのタスクをクリアします。
import java.util.Timer;
import java.util.TimerTask;

public class TestTimer {
public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("    " + "  " + System.currentTimeMillis());
        }
    }, 1000, 2000);

    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    timer.cancel();

}
}
結果:
TimerTaskクラスのcancel()方法は、自身をジョブキューからクリアします。
    final Timer timer = new Timer();
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    TimerTask timerTaskTwo = new TimerTask() {
        @Override
        public void run() {
            System.out.println("Two   ,  " + new Date());
        }
    };
    timer.schedule(timerTask,1000,2000);
    timer.schedule(timerTaskTwo,1000,2000);
    try {
        Thread.sleep(6000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    timerTaskTwo.cancel();
void scheduleは指定された時間に指定されたタスクを実行します。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TestTimer {
public static void main(String[] args) throws ParseException {
    final Timer timer = new Timer();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //      
    String dateString = "2018-06-19 21:19:00";
    String dateStringTwo = "2018-06-19 21:28:00";
    Date date = dateFormat.parse(dateString);
    Date dateTwo = dateFormat.parse(dateStringTwo);
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    TimerTask timerTaskTwo = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    timer.schedule(timerTask, date);
    timer.schedule(timerTaskTwo, dateTwo);

}
}
結果:
すでに経過した時間は実行しますが、到達していない時間などは到着してから実行します。
なぜプログラムの実行が終わってもまだ終わらないですか?
これは、システムがデフォルトでTimerを実行した後、手動で終了していない場合、システムのゴミ収集が呼び出された時にのみ回収が終了するからです。Timer類が持つcancel()を呼び出してTimerの終了を実現します。
void scheduleは、指定された時間から指定されたタスクに対して重複した固定遅延を実行する。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TestTimer {
public static void main(String[] args) throws ParseException {
    final Timer timer = new Timer();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //      
    String dateString = "2018-06-19 21:44:40";
    Date date = dateFormat.parse(dateString);
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    timer.schedule(timerTask, date,2000);

}
}
結果:
void scheduleは、指定された遅延後に指定されたタスクの実行をスケジュールする。
    final Timer timer = new Timer();
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    timer.schedule(timerTask, 2000);
結果:
void scheduleは、指定された遅延後に、固定遅延実行の指定タスクを再実行する。
    final Timer timer = new Timer();
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    timer.schedule(timerTask, 3000,2000);
結果:
void scheduleAtFixedRate(TimeTask task、Date firstTime、long period)は、指定された時間から指定されたタスクに対して重複した固定速度を実行する。
    final Timer timer = new Timer();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //      
    String dateString = "2018-06-19 21:44:40";
    Date date = dateFormat.parse(dateString);
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    //timer.schedule(timerTask, date,2000);
    timer.scheduleAtFixedRate(timerTask, date,2000);
結果:
これまで実行されていなかったタスクはすべて実行されました。
に変更する
timer.schedule(timerTask, date,2000);
//timer.scheduleAtFixedRate(timerTask, date,2000);
結果:
これまで実行されていなかったタスクは実行されませんでした。
void scheduleAt FixedRate(TimeTask task、long delay、long period)は、指定された遅延後に開始し、固定速度の指定タスクを再実行する。
    final Timer timer = new Timer();
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println("   ,  " + new Date());
        }
    };
    timer.scheduleAtFixedRate(timerTask, 3000,2000);
結果:
ソースの探索
無参オブジェクトnew Timer()を作成すると、Timer無参構造体を確認します。
public Timer() {
    this("Timer-" + serialNumber());
}
ここでTimer(String name)のコンストラクタを呼び出して、serialNumber()の方法を調べます。
/**
 * This ID is used to generate thread names.
 */
//     0   
private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
private static int serialNumber() {
    //      1
    return nextSerialNumber.getAndIncrement();
}
認証:
    Timer timer = new Timer();
    Timer timerTwo = new Timer();
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            System.out.println("   ,  " + new Date());
        }
    };
    TimerTask timerTaskTwo = new TimerTask() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            System.out.println("Two   ,  " + new Date());
        }
    };
    timer.schedule(timerTask, 1000);
    timerTwo.schedule(timerTaskTwo, 1000);
結果:
Timerを見る
public Timer(String name) {
    thread.setName(name);
    thread.start();
}
threadを表示
private final TaskQueue queue = new TaskQueue();

private final TimerThread thread = new TimerThread(queue);
TaskQueタスク・キューはタスクすなわちTimeTaskを保存するために使用され、threadはタスクを実行します。
TimerThread類を見る
boolean newTasksMayBeScheduled = true;
newTasMayBeScheduledは、queueタスクのキューが空の場合、タイミングスレッドは直ちに終了するべきかどうかを制御するために使用されます(falseは直ちに終了します)。
private TaskQueue queue;
queueタスクチーム。
public void run() {
    try {
        //   
        mainLoop();
    } finally {
        // Someone killed this Thread, behave as if Timer cancelled
        synchronized(queue) {
            newTasksMayBeScheduled = false;
            queue.clear();  // Eliminate obsolete references
        }
    }
}
最も重要な点
private void mainLoop() {
    while (true) {
        try {
            TimerTask task;
            boolean taskFired;
            //         
            synchronized(queue) {
                //         ,       cancel()
                //      queue ,queue.wait()        queue 
                //    Timer sched()        queue 
                while (queue.isEmpty() && newTasksMayBeScheduled)
                    queue.wait();
                if (queue.isEmpty())
                    break; // Queue is empty and will forever remain; die

                // Queue nonempty; look at first evt and do the right thing
                long currentTime, executionTime;
                task = queue.getMin();
                synchronized(task.lock) {
                    if (task.state == TimerTask.CANCELLED) {
                        queue.removeMin();
                        continue;  // No action required, poll queue again
                    }
                    currentTime = System.currentTimeMillis();
                    executionTime = task.nextExecutionTime;
                    if (taskFired = (executionTime<=currentTime)) {
                        //        ,        
                        //                    
                        if (task.period == 0) { // Non-repeating, remove
                            queue.removeMin();
                            task.state = TimerTask.EXECUTED;
                        } else { 
                            //          ,               ,         
                            queue.rescheduleMin(
                              task.period<0 ? currentTime   - task.period
                                            : executionTime + task.period);
                        }
                    }
                }
                if (!taskFired) // Task hasn't yet fired; wait
                    queue.wait(executionTime - currentTime);
            }
            if (taskFired)  // Task fired; run it, holding no locks
                task.run();
        } catch(InterruptedException e) {
        }
    }
}
ここに来たら、通常の状況でTimerが任務を完了してからずっと終わらない理由が分かります。いつもwhileです。捕獲できない異常やbreakがないと飛び出ません。
if (queue.isEmpty())
    break;
newTasksMayBeScheduled状態を設定してfalseのために飛び出すということです。つまりcancelを呼び出したら、queueは空です。この時直接に外部の死のサイクルから飛び出すので、cancelはこのように実現されます。
Timer類におけるsched方法
以下のすべてのschedule方法はschedメソッドを呼び出しました。
void schedule(TimeTask task,long delay)
void schedule(TimeTask task,Date time)
void schedule(TimeTask task,long delay,long period)
void schedule(TimeTask task,Date first Time,long period)
void scheduleAt FixedRate(TimeTask task、Date first Time、long period)
void scheduleAt FixedRate(TimeTask task、long delay、long period)
private void sched(TimerTask task, long time, long period) {
    if (time < 0)
        throw new IllegalArgumentException("Illegal execution time.");

    // Constrain value of period sufficiently to prevent numeric
    // overflow while still being effectively infinitely large.
    if (Math.abs(period) > (Long.MAX_VALUE >> 1))
        period >>= 1;

    synchronized(queue) {
        //  Timer      
        if (!thread.newTasksMayBeScheduled)
            throw new IllegalStateException("Timer already cancelled.");

        synchronized(task.lock) {
            //TimerTask.VIRGIN         
            if (task.state != TimerTask.VIRGIN)
                throw new IllegalStateException(
                    "Task already scheduled or cancelled");
            //       
            task.nextExecutionTime = time;
            //   
            task.period = period;
            //           
            task.state = TimerTask.SCHEDULED;
        }

        queue.add(task);
        if (queue.getMin() == task)
            queue.notify();
    }
}
どうしてnotify操作をしますか?
メインループの中のwaitなので。
Timer類におけるキャンセル方法
public void cancel() {
    synchronized(queue) {
        thread.newTasksMayBeScheduled = false;
        queue.clear();
        queue.notify();  // In case queue was already empty.
    }
}
newTasMayBeScheduledをfalseに変更し、queueキューのジョブを削除します。
Task Que種類を調べます
private TimerTask[] queue = new TimerTask[128];
TimerTask配列は、初期配列サイズ128である。
int size() {
    return size;
}
size()ジョブキューの長さ
 void add(TimerTask task) {
    // Grow backing store if necessary
    if (size + 1 == queue.length)
        // 2   
        queue = Arrays.copyOf(queue, 2*queue.length);

    queue[++size] = task;
    fixUp(size);
}
add(TimeTask)はタスクを追加するために
TimerTask getMin() {
    return queue[1];
}
getMin()は、現在の並べ替えを取得した後、最近実行したいタスクの一つです。以下を1としてマークします。キューヘッド0は何の操作もしません。
 TimerTask get(int i) {
    return queue[i];
}
get(int i)下付きを指定するデータを取得するには、もちろん下付き0が含まれています。
void removeMin() {
    queue[1] = queue[size];
    queue[size--] = null;  // Drop extra reference to prevent memory leak
    fixDown(1);
}
RemoveMin()は、現在実行されている最も最近のタスクを削除するために、通常は一回のタスクのみをスケジュールし、実行後にこの方法を呼び出すと、TimeTaskをキューから削除することができます。
void quickRemove(int i) {
    assert i <= size;

    queue[i] = queue[size];
    queue[size--] = null;  // Drop extra ref to prevent memory leak
}
quickRmove(int i)指定された要素を削除すると、一般的にはこの方法は呼び出されません。Timerでpureが発生した時だけ、対応するTimerTaskがcancelメソッドを呼び出した時にのみ、この方法が呼び出されます。つまり、あるTimerTaskをキャンセルして、列から削除されます。(ジョブが実行中であるか、それとも実行中であるかは、キューから削除されているが)また、このcancel方法はTimerのcancel方法ではなくTimerTaskであり、一つはスケジューラであり、一つはシングルタスクであり、最後に注意してください。このquickRmoveが完成したら、列の最後の要素をこの位置に補充します。この時、順序が一致しない問題を引き起こします。後で方法があります。
void rescheduleMin(long newTime) {
    queue[1].nextExecutionTime = newTime;
    fixDown(1);
}
recheduleMinは、現在実行されているタスクの次の実行時間を再設定し、列の中で新しい位置から適切な位置に並べ替えて呼び出します。
boolean isEmpty() {
    return size==0;
}
isempty()は空を判定します
void clear() {
    // Null out task references to prevent memory leak
    for (int i=1; i<=size; i++)
        queue[i] = null;

    size = 0;
}
クリーン()クリーニング
fixUp(int k)とfixDown(intk)について方法としては、前者はtaskを追加する場合、まず要素を列の最後に置いて、自分より後に実行するタスクがあるかどうかを前に探してください。もしあるなら、二つのタスクの順番を交換してください。fixDownは正反対です。最初のタスクを実行した後、次の実行時間を得る必要があります。それを順守する必要があります。順序は後のタスクと比較します。
heappify()は、列の後半を全部一回fixeDownの操作をしています。この操作は主にquickRemove方法を取り戻すためです。大量のquickRmoveの後、順番が狂ったら、この時に半分のエリアを一回簡単に並べばいいです。
TimeTask抽象類
//   
final Object lock = new Object();

//task    VIRGIN
int state = VIRGIN;

//         
static final int VIRGIN = 0;

//        。            ,         
static final int SCHEDULED   = 1;

//          (       )      。
static final int EXECUTED    = 2;

//       
static final int CANCELLED   = 3;

//        
long nextExecutionTime;

//       (  )。              。          。  0       。
long period = 0;

//    
public boolean cancel()

//      
public long scheduledExecutionTime() {
    synchronized(lock) {
        return (period < 0 ? nextExecutionTime + period
                           : nextExecutionTime - period);
    }
}
締め括りをつける
Javaマルチスレッドプログラミングコア技術を参照してください。https://blog.csdn.net/xieyuooo/article/details/8607220 https://www.jianshu.com/p/58a5b0853451
Timerクラスには実は内部にthreadがあります。もう一つのqueueがあります。taskは一定の方法でタスクを並べて処理します。
才学が浅いので、何か問題がありましたら、指摘してください。ありがとうございます。