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.


Related articles: