【定时任务】Timer([scheduled task] timer)

什么是Timer?

Timer是线程安排任务以供将来在后台线程中执行的工具。 任务可以安排为一次性执行,或定期重复执行。
对应于每个Timer对象的是一个单独的后台线程,用于按顺序执行所有计时器的任务。 计时器任务应该很快完成。 如果一个计时器任务花费过多的时间来完成,它会“占用”计时器的任务执行线程。 反过来,这可能会延迟后续任务的执行,这些任务可能会“聚集”并在(如果)有问题的任务最终完成时快速连续执行。

  • 之所以这样是因为:
    重复任务的第一次执行的时间、第二次执行的时间、第三….是以及确定好了的。
    比如说:第一次执行是10:00:00 , 每五秒执行一次,那么其实第二次的时间就是 10:00:05,第三次就是10:00:10。但是如果第一次执行的时间就超过了30s,那么当进行如下判断的时候就直接为true,任务执行。而不能达到我们所需要的效果,就是每个任务执行完成后过5s执行下一个任务。
if (taskFired = (executionTime<=currentTime)) {
}

详细如下:

     */
    private void mainLoop() {
        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) {
            }
        }

在对Timer对象的最后一个实时引用消失并且所有未完成的任务都已完成执行后,计时器的任务执行线程将正常终止(并成为垃圾收集的对象)。 但是,这可能需要任意长的时间才能发生。 默认情况下,任务执行线程不作为守护线程运行,因此它能够防止应用程序终止。 如果调用者想快速终止定时器的任务执行线程,调用者应该调用定时器的取消方法。

如果计时器的任务执行线程意外终止,例如,因为它的stop方法被调用,任何进一步尝试在计时器上安排任务都将导致IllegalStateException ,就好像计时器的取消方法已被调用一样。

  • 原因是被安排的任务的状态会被修改:
        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;
            }

此类是线程安全的:多个线程可以共享单个Timer对象,而无需外部同步。

  • 原因是因为任务队列的操作以及单个任务的操作都进行了加锁。

在内部,它使用一个二叉堆来表示它的任务队列,所以调度一个任务的成本是 O(log n),其中 n 是并发调度的任务数。

此类不提供实时保证:它使用Object.wait(long)方法调度任务。
Java 5.0 引入了java.util.concurrent包,其中的并发实用程序之一是ScheduledThreadPoolExecutor ,它是一个线程池,用于以给定的速率或延迟重复执行任务。 它实际上是Timer / TimerTask组合的更通用的替代品,因为它允许多个服务线程,接受各种时间单位,并且不需要子类化TimerTask (只需实现Runnable )。 使用一个线程配置ScheduledThreadPoolExecutor使其等效于Timer 。
实施说明:此类可扩展到大量并发计划任务(数千个应该没有问题)。

Timer中的关键组件

  • TimerThread
    本质上就是一个线程,这个线程的作用就是死循环执行TaskQueue中的任务(线程)。
  • TaskQueue
    本质上可以理解为 以 平衡二叉树 的数据结构组织的 TimerTask数组。(表现为平衡二叉树)
  • TimerTask
    本质上是一个线程,但是包含了其他一些属性,如:任务状态,任务间隔,下次执行时间等。
————————

什么是Timer?

Timer is a tool for threads to schedule tasks for future execution in background threads. Tasks can be scheduled for one-time execution or repeated on a regular basis.
Corresponding to each timer object is a separate background thread, which is used to execute the tasks of all timers in order. The timer task should be completed soon< Strong > If a timer task takes too much time to complete, it will “occupy” the task execution thread of the timer. In turn, this may delay the execution of subsequent tasks, which may “aggregate” and execute quickly and continuously when (if) the problematic task is finally completed

  • This is because:
    The time of the first execution, the time of the second execution, the third… Yes and determined.
    For example, if the first execution is at 10:00:00 every five seconds, the second time is at 10:00:05 and the third time is at 10:00:10. However, if the first execution time exceeds 30s, it will be directly true when making the following judgment, and the task will be executed. We can’t achieve the effect we need, that is, we can execute the next task after 5S after each task is completed.
if (taskFired = (executionTime<=currentTime)) {
}

Details are as follows:

     */
    private void mainLoop() {
        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) {
            }
        }

After the last real-time reference to the timer object disappears and all outstanding tasks have been completed, the task execution thread of the timer will terminate normally (and become a garbage collection object). However, this may take any long time to happen. By default, the task execution thread does not run as a daemon thread, so it can prevent application termination. If the caller wants to quickly terminate the task execution thread of the timer, the caller should call the cancellation method of the timer.

< strong > if the task execution thread of the timer terminates unexpectedly, for example, because its stop method is called, any further attempt to schedule a task on the timer will result in IllegalStateException, as if the cancel method of the timer had been called

  • The reason is that the status of the scheduled task will be modified:
        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;
            }

< strong > this class is thread safe: multiple threads can share a single timer object without external synchronization

  • The reason is that the operation of the task queue and the operation of a single task are locked.

Internally, it uses a binary heap to represent its task queue, so the cost of scheduling a task is O (log n), where n is the number of concurrent scheduled tasks.

This class does not provide real-time assurance: it uses the object. Wait (long) method to schedule tasks.
Java 5.0 introduces the java.util.concurrent package. One of the concurrent utilities is the scheduledthreadpoolexecutor, which is a thread pool used to repeatedly execute tasks at a given rate or delay. It is actually a more general alternative to the timer / TimerTask combination, because it allows multiple service threads to accept various time units, and does not need to subclass TimerTask (just implement runnable). Use a thread to configure the scheduledthreadpoolexecutor to make it equivalent to a timer.
Implementation description: this class can be extended to a large number of concurrent scheduled tasks (thousands should be no problem).

Key components in timer

  • TimerThread
    It is essentially a thread. The role of this thread is to execute tasks (threads) in taskqueue in an endless loop.
  • TaskQueue
    In essence, it can be understood as a TimerTask array organized by a balanced binary tree data structure. (represented as a balanced binary tree)
  • TimerTask
    It is essentially a thread, but it contains other attributes, such as task status, task interval, next execution time, etc.