1. Introduction

Java.util. Timer can set up a background thread to execute either once or repeatedly on a regular basis. Each Timer is a background thread that executes all Timer tasks in sequence. Therefore, Timer should not perform time-consuming operations, otherwise it will cause task accumulation.

The Timer class is thread-safe

Multi-threaded task scheduling, please use: Java. Util. Concurrent. ScheduledThreadPoolExecutor, can completely replace the Timer and TimerTask

Note: All constructors start a timer thread

This uses a binary tree priority queue

2. Internal class TaskQueue

TaskQueue: indicates a TaskQueue with a priority. Tasks are sorted by execution time. The interior is realized by balanced binary heap

2.1 class attribute

Queue: an array of queues compared by TimerTask’s nextExecutionTime

private TimerTask[] queue = new TimerTask[128];
Copy the code

Size: number of tasks in the priority queue, stored in queue[1] to queue[size]

2.2 class method

Size (): Returns the number of tasks currently in the queue

	int size(a) {
        return size;
    }
Copy the code

Add (TimerTask task): Adds a task

    void add(TimerTask task) {
        // Grow backing store if necessary
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2*queue.length);

        queue[++size] = task;
        fixUp(size);
    }
    // After an element is added, the heap adjusts itself to float
    private void fixUp(int k) {
        while (k > 1) {
            int j = k >> 1;
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; }}Copy the code

GetMin () : Gets the top of the heap element

	TimerTask getMin(a) {
        return queue[1];
    }
Copy the code

Get (int I) : Returns the element with the specified subscript

    TimerTask get(int i) {
        return queue[i];
    }
Copy the code

RemoveMin () : Removes the heap top element

void removeMin() { queue[1] = queue[size]; queue[size--] = null; // Drop extra reference to prevent memory leak fixDown(1); } private void fixDown(int k) { int j; while ((j = k << 1) <= size && j > 0) { if (j < size && queue[j].nextExecutionTime > queue[j+1].nextExecutionTime) j++; // j indexes smallest kid if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; }}Copy the code

After removing the top element of the heap, the tail element moves to the top element and then sinks.

QuickRemove (int I) : Removes the ith element

    void quickRemove(int i) {
        assert i <= size;

        queue[i] = queue[size];
        queue[size--] = null;  // Drop extra ref to prevent memory leak
    }
Copy the code

RescheduleMin (Long newTime) : resets the next execution time of the top element

    void rescheduleMin(long newTime) {
        queue[1].nextExecutionTime = newTime;
        fixDown(1);
    }
Copy the code

IsEmpty () : Determines empty

    boolean isEmpty(a) {
        return size==0;
    }
Copy the code

Clear () : clears the queue

    void clear(a) {
        // Null out task references to prevent memory leak
        for (int i=1; i<=size; i++)
            queue[i] = null;

        size = 0;
    }
Copy the code

Heapify () : Initializes the heap

    void heapify(a) {
        for (int i = size/2; i >= 1; i--)
            fixDown(i);
    }
Copy the code

3. Internal class TimerThread

This class implements a thread of execution of timer tasks, waiting on a task queue, and executing tasks, readjust repeated tasks, and remove cancelled and non-repetitive tasks that have already been executed. Thread class inheritance

3.1 class attribute

NewTasksMayBeScheduled: Set to false if the timer has no valid references

   boolean newTasksMayBeScheduled = true;
Copy the code

Queue: A queue of timers, not referring to the Timer object, because it creates a circular reference, the Timer cannot be reclaimed, and the thread will always exist

    private TaskQueue queue;
Copy the code

3.2 class method

Constructor: Pass in the task queue

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }
Copy the code

Run () : thread-specific execution

 public void run(a) {
        try {
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references}}}/** * The main timer loop. (See class comment.) */
    private void mainLoop(a) {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    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 { // Repeating task, reschedule
                                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) {
            }
        }
    }
Copy the code

This large piece of code does three things:

  1. Get the task to execute in the queue
  2. If the task has been canceled or is not a duplicate task, remove it from the queue
  3. Perform a task

4. The Timer class

4.1 class attribute

Queue: indicates the task queue

 private final TaskQueue queue = new TaskQueue();
Copy the code

Thread: indicates the timer thread

private final TimerThread thread = new TimerThread(queue);
Copy the code

NextSerialNumber: indicates the ID of the thread name

private static final AtomicInteger nextSerialNumber = new AtomicInteger(0);
private static int serialNumber(a) {
    return nextSerialNumber.getAndIncrement();
}
Copy the code

4.2 methods

A constructor

    / / no arguments
    public Timer(a) {
        this("Timer-" + serialNumber());
    }
    // Whether it is a background process
    public Timer(boolean isDaemon) {
        this("Timer-" + serialNumber(), isDaemon);
    }
    Create a thread with the specified name
    public Timer(String name) {
        thread.setName(name);
        thread.start();
    }
    public Timer(String name, boolean isDaemon) {
        thread.setName(name);
        thread.setDaemon(isDaemon);
        thread.start();
    }

Copy the code

schedule

    // Specify the task and delay time
    public void schedule(TimerTask task, long delay) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        sched(task, System.currentTimeMillis()+delay, 0);
    }
    // Specify the task and execution time
    public void schedule(TimerTask task, Date time) {
        sched(task, time.getTime(), 0);
    }
    // Specify the task. The first execution is delayed and the subsequent execution cycle is ms
	public void schedule(TimerTask task, long delay, long period) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, System.currentTimeMillis()+delay, -period);
    }
	// Specify the time when the task is executed for the first time
    public void schedule(TimerTask task, Date firstTime, long period) {
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, firstTime.getTime(), -period);
    }

Copy the code

ScheduleAtFixedRate:

	// Specify the task. The first execution is delayed and the subsequent execution cycle is ms
    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, System.currentTimeMillis()+delay, period);
    }
	// Specify the time when the task is executed for the first time
    public void scheduleAtFixedRate(TimerTask task, Date firstTime,
                                    long period) {
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, firstTime.getTime(), period);
    }
Copy the code

Schedule (); mainLoop (); TimerThread (); The next execution time of scheduleAtFixedRate is calculated according to the last calculated execution time. ScheduleAtFixedRate pays more attention to frequency and schedule pays more attention to interval time

queue.rescheduleMin(task.period<0 ? currentTime- task.period : executionTime + task.period);
Copy the code

Sched: The task joins the queue.

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) {
            if(! thread.newTasksMayBeScheduled)throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                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(); }}Copy the code

Cancel: interrupts the timer, which does not affect the ongoing task

    public void cancel(a) {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.}}Copy the code

Purge: Purge all queues of canceled tasks

     public int purge(a) {
         int result = 0;

         synchronized(queue) {
             for (int i = queue.size(); i > 0; i--) {
                 if(queue.get(i).state == TimerTask.CANCELLED) { queue.quickRemove(i); result++; }}if(result ! =0)
                 queue.heapify();
         }

         return result;
     }
Copy the code