In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "how to use Timer in JDK". The content in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn how to use Timer in JDK.
The use of timer Timer
Java.util.Timer is a very useful tool class provided by JDK for tasks that are scheduled to be performed after a specific time and can be performed only once or repeatedly on a regular basis. In JDK, many components use java.util.Timer to implement scheduled tasks or deferred tasks.
Timer can create instances of multiple objects, each with one and only one background thread to execute the task.
The Timer class is thread-safe and multiple threads can share a timer without using any synchronization. Construction method
First, we can take a look at the API document for the constructor of the Timer class
Timer (): create a new timer.
Timer (boolean isDaemon): creates a new timer whose associated worker thread can be specified as a daemon thread.
Timer (String name): creates a new timer whose associated worker thread has the specified name.
Timer (String name, boolean isDaemon): creates a new timer whose associated thread has a specified name and can be specified as a daemon thread.
Note: daemon threads are low-priority threads that perform secondary tasks in the background, such as garbage collection. When a non-daemon thread is running, the Java application does not exit. If all non-daemon threads exit, all daemon threads will exit as well.
Example method
Next let's take a look at the API document for the instance method of the Timer class
Cancel (): terminates this timer and discards all currently executed tasks.
Purge (): removes all cancelled tasks from the task queue of this timer.
Schedule (TimerTask task, Date time): executes a specified task at a specified time.
Schedule (TimerTask task, Date firstTime, long period): from the specified time, the specified task is repeated for a fixed delay time.
Schedule (TimerTask task, long delay): executes the specified task after the specified delay.
Schedule (TimerTask task, long delay, long period): starts after the specified delay and repeats the specified task for a fixed delay.
ScheduleAtFixedRate (TimerTask task, Date firstTime, long period): repeats the specified task at a fixed rate starting from the specified time.
ScheduleAtFixedRate (TimerTask task, long delay, long period): starts after the specified delay and repeats the specified task at a fixed rate.
Both schedule and scheduleAtFixedRate repeat tasks. The difference is that schedule reexecutes the task according to a fixed cycle after the successful execution of the task. For example, if the first task is executed from 0s, the execution time is 5s, and the cycle is 10s, then the next execution time is 15s instead of 10s. On the other hand, scheduleAtFixedRate reexecutes the task at a fixed time from the beginning of the task. For example, if the first task starts from 0s, the execution time is 5s, and the cycle is 10s, then the next execution time is 10s instead of 15s.
Mode of use 1. Execution time is later than current time
Next we will use schedule (TimerTask task, Date time) and schedule (TimerTask task, long delay) to execute the task after 10 seconds, and show the impact of setting the worker thread of Timer as a daemon thread on Timer execution.
First we create the class Task, and then all our operations are performed in this class, using schedule (TimerTask task, Date time) in the class, with the following code
Import java.util.Date;import java.util.Timer;import java.util.TimerTask;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000 Public static void main (String [] args) {Runtime.getRuntime () .addShutdownHook (new Thread (()-> {System.out.println ("Program end time: {0}", currentTimeMillis ());})); long startTimestamp = currentTimeMillis (); System.out.println (format ("Program execution time: {0}", startTimestamp)); Timer timer = new Timer () Timer.schedule (new TimerTask () {@ Override public void run () {long exceptedTimestamp = startTimestamp + 10 * SECOND; long executingTimestamp = currentTimeMillis (); long offset = executingTimestamp-exceptedTimestamp System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset);}}, new Date (startTimestamp + 10 * SECOND);}}
At the beginning of the program, we register the function executed at the end of the program, which is used to print the end time of the program, which we will use later to show the difference between the worker thread as a daemon thread and a non-daemon thread. Next is the main part of the program, we record the execution time of the program, the thread in which the scheduled task is executed, the expected execution time and the actual execution time of the scheduled task.
The actual execution effect of the program after running
The program execution time is: 1614575921461 task runs on the thread [Timer-0], the expected execution time is [1614575931461], the actual execution time is [1614575931464], the actual deviation [3]
The program does not exit after the execution of the scheduled task, and our registered lifecycle function is not executed, which we will explain later.
Next we use schedule (TimerTask task, long delay) in the class to achieve the same effect that is executed after 10 seconds.
Import java.util.Timer;import java.util.TimerTask;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {Runtime.getRuntime () .addShutdownHook (new Thread (())-> {System.out.println (format ("Program end time: {0}", currentTimeMillis () )); Timer timer = new Timer (); long startTimestamp = currentTimeMillis (); System.out.println (format ("Program execution time is: {0}", startTimestamp); timer.schedule (new TimerTask () {@ Override public void run () {long exceptedTimestamp = startTimestamp + 10 * SECOND; long executingTimestamp = currentTimeMillis () Long offset = executingTimestamp-exceptedTimest System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset);}}, 10 * SECOND) }}
The actual execution effect of the program after running
The program execution time is: 1614576593325 the task runs on the thread [Timer-0], the expected execution time is [1614576603325], the actual execution time is [1614576603343], and the actual deviation [18]
Back to the question we just asked, why didn't our program exit normally after performing the timing task? We can find the relevant content in the description of the Thread class in Java API:
From this description, we can see that only in two cases will the Java virtual machine exit execution.
The Runtime.exit () method is called manually, and the security manager allows an exit operation
All non-daemon threads are finished, either after executing the run () method or throwing an uppropagated exception in the run () method
All Timer will create an associated worker thread after creation, which is non-daemon thread by default, so obviously we meet the second condition, so the program will continue to execute without exiting.
So what happens if we set the worker thread of Timer to be a daemon thread?
Import java.util.Timer;import java.util.TimerTask;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {Runtime.getRuntime () .addShutdownHook (new Thread (())-> {System.out.println (format ("Program end time: {0}", currentTimeMillis () ); Timer timer = new Timer (true); long startTimestamp = currentTimeMillis (); System.out.println (format ("Program execution time is: {0}", startTimestamp); timer.schedule (new TimerTask () {@ Override public void run () {long exceptedTimestamp = startTimestamp + 10 * SECOND; long executingTimestamp = currentTimeMillis () Long offset = executingTimestamp-exceptedTimest System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset);}}, 10 * SECOND) }}
The actual execution result after the program is run
Program execution time: 1614578037976 program end time: 1614578037996
You can see that the program is finished before our deferred task starts, because after our main thread exits, all the non-daemon threads end, so the Java virtual machine exits normally instead of waiting for all the tasks in the Timer to finish.
two。 The execution time is earlier than the current time
If we specify the execution time by calculating the Date, it is inevitable that the calculated time is earlier than the current time, which is very common, especially when the Java virtual machine performs garbage collection at inappropriate times, resulting in STW (Stop the world).
Next, we will adjust the code that called schedule (TimerTask task, Date time) so that it executes in the past
Import java.util.Date;import java.util.Timer;import java.util.TimerTask;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000 Public static void main (String [] args) {Runtime.getRuntime () .addShutdownHook (new Thread (()-> {System.out.println ("Program end time: {0}", currentTimeMillis ();})); Timer timer = new Timer (); long startTimestamp = currentTimeMillis (); System.out.println (format ("Program execution time: {0}", startTimestamp)) Timer.schedule (new TimerTask () {@ Override public void run () {long exceptedTimestamp = startTimestamp-10 * SECOND; long executingTimestamp = currentTimeMillis (); long offset = executingTimestamp-exceptedTimestamp System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset);}}, new Date (startTimestamp-10 * SECOND);}}
The execution result after the program is run
The program execution time is: 1614590000184 task runs on the thread [Timer-0], the expected execution time is [1614589990184], the actual execution time is [1614590000203], and the actual deviation [10019]
As you can see, when we specify the elapsed time as the elapsed time, the worker thread of Timer executes the task immediately.
But what happens if we do not calculate the time, but expect to delay the execution of negative time? We will adjust the code that called schedule (TimerTask task, long delay) so that it executes with a negative delay.
Import java.util.Timer;import java.util.TimerTask;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {Runtime.getRuntime () .addShutdownHook (new Thread (())-> {System.out.println (format ("Program end time: {0}", currentTimeMillis () )); Timer timer = new Timer (); long startTimestamp = currentTimeMillis (); System.out.println (format ("Program execution time is: {0}", startTimestamp); timer.schedule (new TimerTask () {@ Override public void run () {long exceptedTimestamp = startTimestamp-10 * SECOND; long executingTimestamp = currentTimeMillis () Long offset = executingTimestamp-exceptedTimest System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset)) },-10 * SECOND);}
The execution result after the program is run
The program execution time is: 1614590267556Exception in thread "main" java.lang.IllegalArgumentException: Negative delay. At java.base/java.util.Timer.schedule (Timer.java:193) at cn.mgdream.schedule.Task.main (Task.java:22)
If we pass in a negative delay time, then Timer throws an exception telling us that a negative delay time cannot be passed in, which seems reasonable-- we pass in the past time because we calculated it, not subjectively. We need to pay attention to this when we use schedule (TimerTask task, long delay).
3. Add multiple tasks to Timer
Next, we will add two delayed tasks to Timer. In order to more easily control the scheduling order and time of the two tasks, we delay the first task by 5 seconds and the second task by 10 seconds, and let the first task block for 10 seconds before ending. In this way, we simulate long tasks.
Import java.util.Timer;import java.util.TimerTask;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {Timer timer = new Timer (); long startTimestamp = currentTimeMillis (); System.out.println ("Program execution time: {0}", startTimestamp)) Timer.schedule (new TimerTask () {@ Override public void run () {try {long exceptedTimestamp = startTimestamp + 5 * SECOND; long executingTimestamp = currentTimeMillis (); long offset = executingTimestamp-exceptedTimestamp System.out.println (format ("Task [0] runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset); Thread.sleep (10 * SECOND) } catch (InterruptedException e) {e.printStackTrace ();}, 5 * SECOND); timer.schedule (new TimerTask () {@ Override public void run () {long exceptedTimestamp = startTimestamp + 10 * SECOND; long executingTimestamp = currentTimeMillis () Long offset = executingTimestamp-exceptedTimest System.out.println (format ("Task [1] runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread () .getName (), exceptedTimestamp, executingTimestamp, offset)) }, 10 * SECOND);}
The execution result after the program is run
Program execution time: 1614597388284 task [0] runs on thread [Timer-0], expected execution time is [1614597393284], actual execution time is [1614597393308], actual deviation [24] task [1] runs on thread [Timer-0], expected execution time is [1614597398284], actual execution time is [1614597403312], actual deviation [5028]
As you can see, the two tasks are executed sequentially in the same thread, while the first task ends 15 seconds after the program starts because it is blocked for 10 seconds, while the second task is expected to end in 10 seconds, but because the first task is not finished yet, the second task starts execution in 15 seconds, which is 5 seconds away from its execution time. When using Timer, try not to perform long tasks or use blocking methods, otherwise it will affect the accuracy of the execution time of subsequent tasks.
4. Perform tasks periodically
Next we will use schedule and scheduleAtFixedRate to implement periodic tasks, respectively. To save space, we will only demonstrate how to use schedule (TimerTask task, long delay, long period) and scheduleAtFixedRate (TimerTask task, long delay, long period) to perform tasks periodically, and introduce the differences between them. The other two methods schedule (TimerTask task, Date firstTime, long period) and scheduleAtFixedRate (TimerTask task, Date firstTime, long period) have the same effect and difference, so I won't repeat them.
First of all, we modify the Task class and call schedule (TimerTask task, long delay, long period) to execute the task periodically after executing the deferred task for the first time.
Import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.atomic.AtomicLong;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {AtomicLong counter = new AtomicLong (0); Timer timer = new Timer (); long startTimestamp = currentTimeMillis () System.out.println (format ("Program execution time is: {0}", startTimestamp); timer.schedule (new TimerTask () {@ Override public void run () {long count = counter.getAndIncrement (); long exceptedTimestamp = startTimestamp + 10 * SECOND + count * SECOND; long executingTimestamp = currentTimeMillis () Long offset = executingTimestamp-exceptedTimest System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset)) }, 10 * SECOND, SECOND);}
The modified code is basically the same as when using schedule (TimerTask task, long delay). We add an additional counter to record the number of times the task is executed, and the method call adds a third parameter, period, which indicates the interval between each task execution and the next execution, which is set to 1 second here.
The execution result after the program is run
Program execution time: 1614609111434 task runs on thread [Timer-0], expected execution time is [1614609121434], actual execution time is [1614609121456], actual deviation [22] task runs on thread [Timer-0], expected execution time is [1614609122434], actual execution time is [1614609122456], actual deviation [22] task runs on thread [Timer-0], expected execution time is [1614609123434] The actual execution time is [1614609123457], the actual deviation [23] the task runs on the thread [Timer-0], the expected execution time is [1614609124434], the actual execution time is [1614609124462], the actual deviation [28] the task runs on the thread [Timer-0], the expected execution time is [1614609125434], the actual execution time is [1614609125467], the actual deviation [33] the task runs on the thread [Timer-0] Expected execution time is [1614609126434], actual execution time is [1614609126470], actual deviation [36] task runs on thread [Timer-0], expected execution time is [1614609127434], actual execution time is [1614609127473], actual deviation [39] task runs on thread [Timer-0], expected execution time is [1614609128434], actual execution time is [1614609128473] Actual deviation [39] the task runs on the thread [Timer-0], the expected execution time is [1614609129434], the actual execution time is [1614609129474], and the actual deviation [40]
It can be seen that each task execution will have a certain time deviation, and this deviation will continue to accumulate with the increase of the number of execution. This time deviation depends on the number of tasks that need to be performed in Timer, and there is a non-decreasing trend as the number of tasks that need to be performed in Timer increases. Because only one task is being repeated in this program, the deviation of each execution is not very large, and the time deviation will become obvious if hundreds of tasks are maintained at the same time.
Next, we modify the Task class and call scheduleAtFixedRate (TimerTask task, long delay, long period) to execute the task periodically.
Import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.atomic.AtomicLong;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {AtomicLong counter = new AtomicLong (0); Timer timer = new Timer (); long startTimestamp = currentTimeMillis () System.out.println (format ("Program execution time is: {0}", startTimestamp); timer.scheduleAtFixedRate (new TimerTask () {@ Override public void run () {long count = counter.getAndIncrement (); long exceptedTimestamp = startTimestamp + 10 * SECOND + count * SECOND; long executingTimestamp = currentTimeMillis () Long offset = executingTimestamp-exceptedTimest System.out.println (format ("Task runs on thread [{0}], expected execution time is [{1}], actual execution time is [{2}], actual deviation [{3}]", currentThread (). GetName (), exceptedTimestamp, executingTimestamp, offset)) }, 10 * SECOND, SECOND);}
The effects of methods scheduleAtFixedRate (TimerTask task, long delay, long period) and schedule (TimerTask task, long delay) are basically the same, and they can both achieve the effect of executing tasks periodically, but the scheduleAtFixedRate method modifies the next expected execution time of the task, and calculates the next expected execution time according to the expected execution time of each time plus the period parameter, so scheduleAtFixedRate is repeated at a fixed rate. Schedule, on the other hand, only guarantees the same interval between the two executions.
The execution result after the program is run
Program execution time: 1614610372927 task runs on thread [Timer-0], expected execution time is [1614610383927], actual execution time is [1614610383950], actual deviation [23] task runs on thread [Timer-0], expected execution time is [1614610384927], actual execution time is [1614610384951], actual deviation [24] task runs on thread [Timer-0], expected execution time is [1614610385927] The actual execution time is [1614610385951], the actual deviation [24] the task runs on the thread [Timer-0], the expected execution time is [1614610386927], the actual execution time is [1614610386947], the actual deviation [20] the task runs on the thread [Timer-0], the expected execution time is [1614610387927], the actual execution time is [1614610387949], the actual deviation [22] the task runs on the thread [Timer-0] Expected execution time is [1614610388927], actual execution time is [1614610388946], actual deviation [19] task runs on thread [Timer-0], expected execution time is [1614610389927], actual execution time is [1614610389946], actual deviation [19] task runs on thread [Timer-0], expected execution time is [1614610390927], actual execution time is [1614610390947] Actual deviation [20] task runs on thread [Timer-0], expected execution time is [1614610391927], actual execution time is [1614610391950], actual deviation [23] task runs on thread [Timer-0], expected execution time is [1614610392927], actual execution time is [1614610392946], actual deviation [19] 5. Stop the task
Although we rarely take the initiative to stop a task, here is a description of how to stop a task.
There are two ways to stop a task: stop a single task and stop the entire Timer.
First of all, we show how to stop a single task. In order to stop a single task, we need to call the cancal () method of TimerTask and the purge () method of Timer to remove all tasks that have been stopped (recall that too many stopped tasks are not empty will affect our execution time)
Import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.atomic.AtomicLong;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task {private static final long SECOND = 1000; public static void main (String [] args) {AtomicLong counter = new AtomicLong (0); Timer timer = new Timer (); long startTimestamp = currentTimeMillis () System.out.println (format ("Program execution time: {0}", startTimestamp)); TimerTask [] timerTasks = new TimerTask [4096]; for (int I = 0; I)
< timerTasks.length; i++) { final int serialNumber = i; timerTasks[i] = new TimerTask() { @Override public void run() { long count = counter.getAndIncrement(); long exceptedTimestamp = startTimestamp + 10 * SECOND + count * SECOND; long executingTimestamp = currentTimeMillis(); long offset = executingTimestamp - exceptedTimestamp; System.out.println(format("任务[{0}]运行在线程[{1}]上, 期望执行时间为[{2}], 实际执行时间为[{3}], 实际偏差[{4}]", serialNumber, currentThread().getName(), exceptedTimestamp, executingTimestamp, offset)); } }; } for (TimerTask timerTask : timerTasks) { timer.schedule(timerTask, 10 * SECOND, SECOND); } for (int i = 1; i < timerTasks.length; i++) { timerTasks[i].cancel(); } timer.purge(); }} 首先我们创建了4096个任务,并让Timer来调度它们,接下来我们把除了第0个任务外的其他4095个任务停止掉,并从Timer中移除所有已经停止的任务。 程序运行后的执行结果 程序执行时间为: 1,614,611,843,830任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,853,830], 实际执行时间为[1,614,611,853,869], 实际偏差[39]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,854,830], 实际执行时间为[1,614,611,854,872], 实际偏差[42]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,855,830], 实际执行时间为[1,614,611,855,875], 实际偏差[45]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,856,830], 实际执行时间为[1,614,611,856,876], 实际偏差[46]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,857,830], 实际执行时间为[1,614,611,857,882], 实际偏差[52]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,858,830], 实际执行时间为[1,614,611,858,883], 实际偏差[53]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,859,830], 实际执行时间为[1,614,611,859,887], 实际偏差[57]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,860,830], 实际执行时间为[1,614,611,860,890], 实际偏差[60]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,861,830], 实际执行时间为[1,614,611,861,891], 实际偏差[61]任务[0]运行在线程[Timer-0]上, 期望执行时间为[1,614,611,862,830], 实际执行时间为[1,614,611,862,892], 实际偏差[62] 我们可以看到,只有第0个任务再继续执行,而其他4095个任务都没有执行。 接下来我们介绍如何使用Timer的cancel()来停止整个Timer的所有任务,其实很简单,只需要执行timer.cancel()就可以。 import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.atomic.AtomicLong;import static java.lang.System.currentTimeMillis;import static java.lang.Thread.currentThread;import static java.text.MessageFormat.format;public class Task { private static final long SECOND = 1000; public static void main(String[] args) { Runtime.getRuntime().addShutdownHook(new Thread(() ->{System.out.println (format ("Program end time is: {0}", currentTimeMillis ());})); AtomicLong counter = new AtomicLong (0); Timer timer = new Timer (); long startTimestamp = currentTimeMillis (); System.out.println ("Program execution time is: {0}", startTimestamp); TimerTask [] timerTasks = new TimerTask [4096] For (int I = 0; I
< timerTasks.length; i++) { final int serialNumber = i; timerTasks[i] = new TimerTask() { @Override public void run() { long count = counter.getAndIncrement(); long exceptedTimestamp = startTimestamp + 10 * SECOND + count * SECOND; long executingTimestamp = currentTimeMillis(); long offset = executingTimestamp - exceptedTimestamp; System.out.println(format("任务[{0}]运行在线程[{1}]上, 期望执行时间为[{2}], 实际执行时间为[{3}], 实际偏差[{4}]", serialNumber, currentThread().getName(), exceptedTimestamp, executingTimestamp, offset)); } }; } timer.cancel(); }} 在将所有的任务添加到Timer后,我们执行Timer对象的cancel()方法,为了更方便地表现出Timer的工作线程也终止了,我们注册了生命周期方法,来帮我们在程序结束后打印结束时间。 程序运行后的执行结果 程序执行时间为: 1,614,612,436,037程序结束时间为: 1,614,612,436,061 可以看到,在执行Timer对象的cancel()方法后,Timer的工作线程也随之结束,程序正常退出。 源码解析TimerTask TimerTask类是一个抽象类,实现了Runnable接口 public abstract class TimerTask implements RunnableTimerTask对象的成员 首先来看TimerTask类的成员部分 final Object lock = new Object();int state = VIRGIN;static final int VIRGIN = 0;static final int SCHEDULED = 1;static final int EXECUTED = 2;static final int CANCELLED = 3;long nextExecutionTime;long period = 0; 对象lock是对外用来控制TimerTask对象修改的锁对象,它控制了锁的粒度--只会影响类属性的变更,而不会影响整个类的方法调用。接下来是state属性表示TimerTask对象的状态。nextExecutionTime属性表示TimerTask对象的下一次执行时间,当TimerTask对象被添加到任务队列后,将会使用这个属性来按照从小到大的顺序排序。period属性表示TimerTask对象的执行周期,period属性的值有三种情况 如果是0,那么表示任务不会重复执行 如果是正数,那么就表示任务按照相同的执行间隔来重复执行 如果是负数,那么就表示任务按照相同的执行速率来重复执行 TimerTask对象的构造方法 Timer对象的构造方法很简单,就是protected限定的默认构造方法,不再赘述 protected TimerTask() {}TimerTask对象的成员方法 接下来我们看下TimerTask对象的成员方法 public abstract void run();public boolean cancel() { synchronized(lock) { boolean result = (state == SCHEDULED); state = CANCELLED; return result; }}public long scheduledExecutionTime() { synchronized(lock) { return (period < 0 ? nextExecutionTime + period : nextExecutionTime - period); }} 首先是run()方法实现自Runnable()接口,为抽象方法,所有的任务都需要实现此方法。接下来是cancel()方法,这个方法会将任务的状态标记为CANCELLED,如果在结束前任务处于被调度状态,那么就返回true,否则返回false。至于scheduledExecutionTime()只是用来计算重复执行的下一次执行时间,在Timer中并没有被使用过,不再赘述。 TimerQueue TimerQueue是Timer维护任务调度顺序的最小优先队列,使用的是最小二叉堆实现,如上文所述,排序用的Key是TimerTask的nextExecutionTime属性。 在介绍TimerQueue之前,我们先补充下数据结构的基础知识 二叉堆(Binary heap) 二叉堆是一颗除了最底层的元素外,所有层都被填满,最底层的元素从左向右填充的完全二叉树(complete binary tree)。完全二叉树可以用数组表示,假设元素从1开始编号,下标为i的元素,它的左孩子的下标为2*i,它的右孩子的下标为2*i+1。 二叉堆的任意非叶节点满足堆序性:假设我们定义的是最小优先队列,那么我们使用的是小根堆,任意节点的元素值都小于它的左孩子和右孩子(如果有的话)的元素值。 二叉堆的定义满足递归定义法,即二叉堆的任意子树都是二叉堆,单个节点本身就是二叉堆。 根据堆序性和递归定义法,二叉堆的根节点一定是整个二叉堆中元素值最小的节点。 与堆结构有关的操作,除了add, getMin和removeMin之外,还有fixUp、fixDown和heapify三个关键操作,而add、getMin和removeMin也是通过这三个操作来完成的,下面来简单介绍下这三个操作 fixUp: 当我们向二叉堆中添加元素时,我们可以简单地将它添加到二叉树的末尾,此时从这个节点到根的完整路径上不满足堆序性。之后将它不断向上浮,直到遇到比它小的元素,此时整个二叉树的所有节点都满足堆序性。当我们减少了二叉堆中元素的值的时候也可以通过这个方法来维护二叉堆。 fixDown: 当我们从二叉堆中删除元素时,我们可以简单地将二叉树末尾的元素移动到根,此时不一定满足堆序性,之后将它不断下沉,直到遇到比它大的元素,此时整个二叉树的所有节点都满足堆序性。当我们增加了二叉堆中元素的值的时候也可以通过这个方法来维护二叉堆。 heapify: 当我们拿到无序的数组的时候,也可以假设我们拿到了一棵不满足堆序性的二叉树,此时我们将所有的非叶节点向下沉,直到整个二叉树的所有节点都满足堆序性,此时我们得到了完整的二叉堆。这个操作是原地操作,不需要额外的空间复杂度,而时间复杂度是O(N)。 关于二叉堆的详细内容将会在后续的文章中展开详解,这里只做简单的介绍,了解这些我们就可以开始看TimerQueue的源码。 TimerQueue的完整代码 我们直接来看TaskQueue的完整代码 class TaskQueue { private TimerTask[] queue = new TimerTask[128]; private int size = 0; int size() { return size; } 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); } TimerTask getMin() { return queue[1]; } TimerTask get(int i) { return queue[i]; } void removeMin() { queue[1] = queue[size]; queue[size--] = null; // Drop extra reference to prevent memory leak fixDown(1); } void quickRemove(int i) { assert i >1; if (queue [j] .nextExecutionTime
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.