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:
- Get the task to execute in the queue
- If the task has been canceled or is not a duplicate task, remove it from the queue
- 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