java implements multi threaded timer task
- 2020-06-12 08:58:52
- OfStack
In Java, Timer is a utility class in the java.util package that provides timer functionality. We can create an Timer object and then call its schedule method to perform a specific task at a specific time. And you can have it perform a task at a specific frequency of 1, which is described as TimerTask, and we just need to write the operation that we're going to do in the run method of the TimerTask class. I'll start with two small examples to let you know what a timer is. Then analyze one of the source code implementation.
The first small example:
package com.zkn.newlearn.thread;
import java.util.Timer;
import java.util.TimerTask;
/**
* Traditional timers
* Created by zkn on 2016/11/1.
*/
public class TraditionalTimerTest01 {
public static void main(String[] args){
//TimerTask is Runnable Of the interface 1 The implementation class is, it is 1 A smoke like classes
//schedule is 1 Three overloaded methods: the 1 A parameter TimerTask Implementation class.
// The first 2 The number one parameter is number one 1 The execution time.
// The first 3 The parameters are interval time
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println(" This is a 1 Three timer tasks !");
}
},1000,2000);
}
}
Second small example: Have task 1 execute every 4 seconds and task 2 every 2 seconds. Repeat in turn.
package com.zkn.newlearn.thread;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by zkn on 2016/11/1.
*/
public class TraditionalTimerTest02 {
public static void main(String[] args){
new Timer().schedule(new MyTimerTask01(),4000);
}
private static class MyTimerTask01 extends TimerTask{
@Override
public void run() {
System.out.println(" I am a TimerTask1 I was executed !");
new Timer().schedule(new MyTimerTask02(),2000);
}
}
private static class MyTimerTask02 extends TimerTask {
@Override
public void run() {
System.out.println(" I am a TimerTask2 I was executed !");
new Timer().schedule(new MyTimerTask01(),4000);
}
}
}
You're going to be wondering how does the timer work? Let's look at the main code in Timer in section 1.
private final TaskQueue queue = new TaskQueue();
/**
* The timer thread.
*/
private final TimerThread thread = new TimerThread(queue);
Notice that two pieces of code are important pieces of code. Both TaskQueue and TimerThread are inner classes of Timer. TaskQueue is a priority queue for performing tasks. TimerThread is a thread class that inherits from Thread. Both of them play a crucial role in the timer, which is basically supported by these two classes. Next, let's look at the construction method of Timer:
public Timer(String name) {
thread.setName(name);
thread.start();
}
public Timer() {
this("Timer-" + serialNumber());
}
The constructor that has no arguments will call the constructor that has arguments, what do you see in the constructor that has arguments? Does this look familiar? That's right, at new Timer(), a thread is started. The object that started this thread is TimerThread above! Let's take a look at what run's method does in 1:
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
The run method does two main things: 1. Call the loop method mainLoop(), which we will examine in detail below; 2. 2: finally block terminates timed task. There is nothing to say about terminating the timed task, but let's focus on the mainLoop() method in 1.
private void mainLoop() {
while (true) { // Start a dead cycle
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// If the task queue is empty and the timed task has not been canceled, the thread is suspended Wait for the task to arrive
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // If there are no more tasks in the task queue, the loop ends and the task ends
// If there is a task in the queue, move on
long currentTime, executionTime;
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // If the mission is canceled Removes the current task. This will requeue the order of tasks in the column
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // If you just execute 1 The second time, after the completion of the execution, the completion of the task
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // If the task is performed at a fixed frequency, the time of the next execution is calculated
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Not until the mission is executed Waiting for a thread call
queue.wait(executionTime - currentTime);
}
if (taskFired) // It's time for the task to execute run Method, perform the task
task.run();
} catch(InterruptedException e) {
}
}
}
This class is quite long, and I have noted the specific operations in the comments. This class basically does a few things: loop through the tasks in the task queue and execute the tasks in the queue. When is a task put on the execution queue? In the schedule method. Let's look at the implementation of schedule:
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0) // If the first 1 The execution time is less than 0 An exception is thrown
throw new IllegalArgumentException("Negative delay.");
if (period <= 0) // The interval is less than or equal to 0 An exception is thrown
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -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. This interval is almost impossible to execute until death
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled) // In the execution method of a task If the timer task has been canceled Throw an exception
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) { //object Object lock
if (task.state != TimerTask.VIRGIN) // It's time to start on a mission The status of the task should be 0 the
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time; // Next execution time In the above mainLoop Method
task.period = period; // Set the task interval time on the above mainLoop Method
task.state = TimerTask.SCHEDULED; // The scheduling method is called Set the status of the timer task to Scheduled not executed
}
queue.add(task); // Add the execution task to the task queue
if (queue.getMin() == task)
queue.notify(); // If the number in the task queue 1 If the task is the current task, the current task is put into the equal lock pool Waiting for
}
}
shedule does the simple thing. The primary role is to put TimerTask on the task queue.
Here's a quick look at the TaskQueue code:
class TaskQueue {
// define 1 a TimerTask The heap array <span style="white-space:pre"> </span>
private TimerTask[] queue = new TimerTask[128];
// Number of tasks in the task queue <span style="white-space:pre"> </span>
private int size = 0;
int size() {
return size;
}
// Add tasks to the priority queue It will expand the array if it's not long enough
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);
}
// Gets the priority task
TimerTask getMin() {
return queue[1];
}
TimerTask get(int i) {
return queue[i];
}
// Removal comes first 1 A bit of a task that cannot be performed
void removeMin() {
queue[1] = queue[size];
queue[size--] = null; // Drop extra reference to prevent memory leak Empty the object Waiting for the gc recycling
fixDown(1);
}
// Delete tasks from the task queue Used to here 1 An assertion To judge i Is not greater than size
void quickRemove(int i) {
assert i <= size;
queue[i] = queue[size];
queue[size--] = null; // Drop extra ref to prevent memory leak
}
// Reset the execution time of the priority task And reorder the task queue To ensure the highest priority Priority to be executed
void rescheduleMin(long newTime) {
queue[1].nextExecutionTime = newTime;
fixDown(1);
}
boolean isEmpty() {
return size==0;
}
// Clear the task queue Timed task end
void clear() {
// Null out task references to prevent memory leak
for (int i=1; i<=size; i++)
queue[i] = null;
size = 0;
}
// Two heap sort Select the highest priority task to perform
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;
}
}
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;
}
}
void heapify() {
for (int i = size/2; i >= 1; i--)
fixDown(i);
}
}
OK, here the timing task of the source code analysis has been completed.