In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article is about how to schedule repetitive tasks in Java applications. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.
The java.util.Timer and java.util.TimerTask classes (which I collectively call the timer framework of Java) make it easy for programmers to schedule simple tasks. Note that these classes are also available in J2ME. Before introducing this framework in Java 2 SDK Standard Edition 1.3, developers had to write their own schedulers, which involved the complexity of processing threads and the Object.wait () method. However, the Java timer framework is not rich enough to meet the scheduling needs of many applications. Even tasks that need to be repeated at the same time every day cannot be scheduled directly using Timer, because time jumps occur when daylight saving time comes and goes.
Schedule one-time tasks
The scheduling framework is built on top of the Java timer framework class. Therefore, before explaining how the scheduling framework is used and implemented, we will first understand how to use these classes for scheduling.
Imagine an egg timer that tells you when a few minutes have passed by playing a sound (so your eggs are boiled). The code in listing 1 forms the basis of a simple egg timer written in the Java language:
Listing 1. EggTimer class package org.tiling.scheduling.examples;import java.util.Timer;import java.util.TimerTask;public class EggTimer {private final Timer timer = new Timer (); private final int minutes; public EggTimer (int minutes) {this.minutes = minutes;} public void start () {timer.schedule (new TimerTask () {public void run () {playSound () Timer.cancel ();} private void playSound () {System.out.println ("Your egg is ready!"); / / Start a new thread to play a sound... }, minutes ∗ 60 ∗ 1000);} public static void main (String [] args) {EggTimer eggTimer = new EggTimer (2); eggTimer.start ();}}
An EggTimer instance has a Timer instance to provide the necessary scheduling. When the egg timer is started using the start () method, it arranges aTimerTask to execute after the specified number of minutes. When the time is up, the run () method TimerTask on the is called by the Timer behind the scenes to play the sound. The application then terminates after the timer is cancelled.
Schedule repetitive tasks
Timer allows you to schedule repeated execution of tasks by specifying a fixed execution rate or a fixed delay between execution. However, there are many applications that have more complex scheduling requirements. For example, an alarm clock that rings a wake-up call at the same time every morning cannot simply use a fixed rate schedule of 86400000 milliseconds (24 hours), because in the days when the clock goes forward, the alarm clock will be too late or too early or backward (if your time zone uses daylight saving time). The solution is to use the calendar algorithm to calculate the next scheduled occurrence of daily events. This is what the scheduling framework supports. Consider the implementation in AlarmClock listing 2 (see the related link to download the source code for the scheduling framework, as well as the JAR file containing the framework and examples):
Listing 2. AlarmClock class package org.tiling.scheduling.examples;import java.text.SimpleDateFormat;import java.util.Date;import org.tiling.scheduling.Scheduler;import org.tiling.scheduling.SchedulerTask;import org.tiling.scheduling.examples.iterators.DailyIterator;public class AlarmClock {private final Scheduler scheduler = new Scheduler (); private final SimpleDateFormat dateFormat = new SimpleDateFormat ("dd MMM yyyy HH:mm:ss.SSS"); private final int hourOfDay, minute, second Public AlarmClock (int hourOfDay, int minute, int second) {this.hourOfDay = hourOfDay; this.minute = minute; this.second = second;} public void start () {scheduler.schedule (new SchedulerTask () {public void run () {soundAlarm () } private void soundAlarm () {System.out.println ("Wake up!" + "It's" + dateFormat.format (new Date (); / / Start a new thread to sound an alarm... }, new DailyIterator (hourOfDay, minute, second);} public static void main (String [] args) {AlarmClock alarmClock = new AlarmClock (7,0,0); alarmClock.start ();}}
Notice how similar the code is to the egg timer application. The AlarmClock instance has a Scheduler instance (rather than a Timer) to provide the necessary scheduling. When activated, the alarm clock will arrange a SchedulerTask (instead of a TimerTask) to play the alarm clock. Instead of scheduling task execution after a fixed delay, the alarm clock uses a DailyIterator class to describe its schedule. In this case, it just arranges the task at 7:00 every morning. This is the output of a typical run:
Wake up! It's 24 Aug 2003 07:00:00.023Wake up! It's 25 Aug 2003 07:00:00.001Wake up! It's 26 Aug 2003 07:00:00.058Wake up! It's 27 Aug 2003 07:00:00.015Wake up! It's 28 Aug 2003 07:00:00.002...
DailyIterator implements the ScheduleIterator interface, which specifies the planned execution time of SchedulerTask as a series of java.util.Date objects. The next () method then iterates over the objects in chronological order. The return value of null causes the task to be canceled (that is, it will never run again)-- in fact, an attempt to reschedule will result in an exception being thrown. Listing 3 contains the ScheduleIterator interface:
Listing 3. ScheduleIterator interface package org.tiling.scheduling;import java.util.Date;public interface ScheduleIterator {public Date next ();}
The next () method of DailyIterator returns an object that Date represents at the same time every day (7:00), as shown in listing 4. So if you call next () a newly constructed DailyIterator class, you will get the constructor passed in at 7:00 on or after that date. The subsequent call to next () will return at 7:00 the next few days and will be repeated forever. To implement this behavior, DailyIterator uses a java.util.Calendar instance. The constructor sets the calendar so that the first call to next () returns the correct one, and Date only needs to add one day to the calendar. Note that the code does not explicitly mention daylight saving time correction; it is not needed because the Calendar implementation (in this case GregorianCalendar) handles this issue.
Listing 4. The DailyIterator class package org.tiling.scheduling.examples.iterators;import org.tiling.scheduling.ScheduleIterator;import java.util.Calendar;import java.util.Date;/ ∗∗ ∗ A DailyIterator class returns a sequence of dates on subsequent days ∗ representing the same time each day. ∗ / public class DailyIterator implements ScheduleIterator {private final int hourOfDay, minute, second; private final Calendar calendar = Calendar.getInstance (); public DailyIterator (int hourOfDay, int minute, int second) {this (hourOfDay, minute, second, new Date ());} public DailyIterator (int hourOfDay, int minute, int second, Date date) {this.hourOfDay = hourOfDay; this.minute = minute; this.second = second; calendar.setTime (date) Calendar.set (Calendar.HOUR_OF_DAY, hourOfDay); calendar.set (Calendar.MINUTE, minute); calendar.set (Calendar.SECOND, second); calendar.set (Calendar.MILLISECOND, 0); if (! calendar.getTime (). Before (date)) {calendar.add (Calendar.DATE,-1) }} public Date next () {calendar.add (Calendar.DATE, 1); return calendar.getTime ();}} implement the scheduling framework
In the previous section, we learned how to use the scheduling framework and compared it to the Java timer framework. Next, I'll show you how the framework is implemented. In addition to ScheduleIterator in listing 3 of the display interface, there are two other classes-a framework of Scheduler and SchedulerTask-. These classes actually use Timer and TimerTask under the cover, because the schedule is really just a series of one-time timers. Listings 5 and 6 show the source code for these two classes:
Listing 5. Scheduler package org.tiling.scheduling;import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class Scheduler {class SchedulerTimerTask extends TimerTask {private SchedulerTask schedulerTask; private ScheduleIterator iterator; public SchedulerTimerTask (SchedulerTask schedulerTask, ScheduleIterator iterator) {this.schedulerTask = schedulerTask; this.iterator = iterator } public void run () {schedulerTask.run (); reschedule (schedulerTask, iterator);}} private final Timer timer = new Timer (); public Scheduler () {} public void cancel () {timer.cancel ();} public void schedule (SchedulerTask schedulerTask, ScheduleIterator iterator) {Date time = iterator.next () If (time = = null) {schedulerTask.cancel ();} else {synchronized (schedulerTask.lock) {if (schedulerTask.state! = SchedulerTask.VIRGIN) {throw new IllegalStateException ("Task already scheduled" + "or cancelled");} schedulerTask.state = SchedulerTask.SCHEDULED SchedulerTask.timerTask = new SchedulerTimerTask (schedulerTask, iterator); timer.schedule (schedulerTask.timerTask, time);}} private void reschedule (SchedulerTask schedulerTask, ScheduleIterator iterator) {Date time = iterator.next (); if (time = = null) {schedulerTask.cancel () } else {synchronized (schedulerTask.lock) {if (schedulerTask.state! = SchedulerTask.CANCELLED) {schedulerTask.timerTask = new SchedulerTimerTask (schedulerTask, iterator); timer.schedule (schedulerTask.timerTask, time);}
Listing 6 shows the source code for the SchedulerTask class:
Listing 6. SchedulerTaskpackage org.tiling.scheduling;import java.util.TimerTask;public abstract class SchedulerTask implements Runnable {final Object lock = new Object (); int state = VIRGIN; static final int VIRGIN = 0; static final int SCHEDULED = 1; static final int CANCELLED = 2; TimerTask timerTask; protected SchedulerTask () {} public abstract void run () Public boolean cancel () {synchronized (lock) {if (timerTask! = null) {timerTask.cancel ();} boolean result = (state = = SCHEDULED); state = CANCELLED; return result }} public long scheduledExecutionTime () {synchronized (lock) {return timerTask = = null? 0: timerTask.scheduledExecutionTime ();}
Just like egg timers, each instance of the scheduler has an instance of the timer to provide the underlying scheduling. Unlike a single one-time timer used to implement an egg timer, the scheduler concatenates one-time timers to execute the SchedulerTask class at the time specified by ScheduleIterator.
Consider the scheduler's public schedule () method-- this is the entry point for scheduling, because it is the method called by the client. (the only other public method, cancel (), is described in Canceling tasks. The time SchedulerTask for the first execution, by calling the next () method on the ScheduleIterator interface. The scheduling is then started by calling the one-shot schedule () method on the underlying Timer class while executing at this time. The TimerTask object provided for one-time execution is an instance of a nested SchedulerTimerTask class that packages tasks and iterators. Within the allotted time, the run () method is called on the nested class, which uses packaged tasks and iterator references to reschedule the next execution of the task. The reschedule () method is very similar to the schedule () method, except that it is private and performs a slightly different set of state checks on SchedulerTask. The rescheduling process repeats indefinitely, constructing a new instance of the nested class for each scheduled execution until the task or scheduler is canceled (or JVM is closed).
Like its TimerTask counterpart, SchedulerTask goes through a series of states in its life cycle. When created, it is in a VIRGIN state, which means that it has never been scheduled. Once scheduled, it moves to a SCHEDULED state, and if the task is cancelled by one of the methods described below, it then switches to the CANCELLED state. Managing correct state transitions, such as ensuring that non-VIRGIN tasks are not scheduled twice, adds additional complexity to the Scheduler and SchedulerTask classes. Whenever you perform an operation that may change the state of a task, the code must be synchronized on the locked object of the task.
Cancel the task
There are three ways to cancel a scheduled task. The first is to call the cancel () method on SchedulerTask. This is like calling cancel () on TimerTask: the task will never run again, although if it is already running, it will run until it is complete. The return value of the cancel () method is a Boolean value indicating whether further scheduled tasks will be run if cancel () has not been called. More precisely, if the task is in the SCHEDULED state immediately before calling cancel (), it returns true. If you try to reschedule cancelled (or even scheduled) tasks, Scheduler will throw IllegalStateException.
The second way to cancel a scheduled task is for ScheduleIterator to return null. This is only a shortcut to the first, because the Scheduler class calls cancel () on the SchedulerTask class. Canceling a task in this way is useful if you want the iterator (not the task) to control when the scheduling stops.
The third way is to cancel the overall Scheduler by calling its cancel () method. This cancels all tasks for the scheduler and puts it in a state where no more tasks can be scheduled.
Extend the cron tool
The scheduling framework can be compared to the UNIX cron tool, except that the specification of scheduling time is imperative control rather than declarative control. For example, the DailyIterator class uses the same schedule as the cron job in the AlarmClock implementation, specified by the crontab entry starting at 0 7 *. (these fields specify the minute, hour, day of the month, month, and day of the week, respectively. )
However, the scheduling framework is more flexible than cron. Imagine a HeatingController application that turns on hot water in the morning. I want to instruct it to turn on the hot water at 8:00 on weekdays and 9:00 on weekends. To use cron, I need two crontab entries (0 8 * * 1, 2, 3, 4, 5 and 0 9 * 6, 7). By using ScheduleIterator, the solution is more elegant because I can use combinations to define a single iterator. Listing 7 shows one method:
Listing 7. Use a combination to define a single iterator int [] weekdays = new int [] {Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY}; int [] weekend = new int [] {Calendar.SATURDAY, Calendar.SUNDAY} ScheduleIterator I = new CompositeIterator (new ScheduleIterator [] {new RestrictedDailyIterator (8,0,0, weekdays), new RestrictedDailyIterator (9,0,0, weekend)})
A RestrictedDailyIterator class is like DailyIterator, except that it is limited to running on a specific day of the week; and a CompositeIterator class takes a set of ScheduleIterators and sorts the dates correctly into a single schedule.
There are many other scheduling cron that cannot be generated, but ScheduleIterator can be implemented. For example, the schedule described in "the last day of each month" can be implemented using the standard Java calendar algorithm (using the Calendar class), while using cron. The application doesn't even have to use the Calendar class. In the source code for this article, I provide an example of a safety light controller that runs according to the schedule of "turn on the lights 15 minutes before sunset." The implementation uses the Calendrical Calculations software package to calculate the local sunset time (given latitude and longitude).
Real-time guarantee
When writing applications that use scheduling, it is important to understand the framework's commitment to timeliness. Will my mission be carried out ahead of schedule or delayed? If so, what is the maximum margin of error? Unfortunately, there are no simple answers to these questions. In practice, however, this behavior is good enough for a wide range of applications. The following discussion assumes that the system clock is correct (see related links for information about network time protocols).
Because Scheduler delegates its scheduling to the Timer class, the real-time guarantee that Scheduler can make is similar to that of Timer. Timer uses this Object.wait (long) method to schedule tasks. The current thread waits until awakened, which may be due to one of the following reasons:
The notify () or notifyAll () method is called an object that passes through another thread.
The thread was interrupted by another thread.
The thread is awakened without notice. (false awakening)
The specified time has passed.
The first possibility does not occur on the Timer class because the object on which wait () is called is private. Even so, Timer implements safeguards against early awakening for the first three reasons, ensuring that threads wake up after time has passed. Now, the documentation comment Object.wait (long) indicates that it may wake up after "more or less" time has passed, so the thread may wake up in advance. In this case, Timer issues another wait () of (scheduledExecutionTime-System.currentTimeMillis ()) milliseconds, ensuring that the task can never be executed early.
Can the task be delayed? Right. There are two main reasons for delayed execution: thread scheduling and garbage collection.
The Java language specification is deliberately ambiguous in online program scheduling. This is because the Java platform is universal, targeting a wide range of hardware and related operating systems. Although most JVM implementations have a fair thread scheduler, this is not guaranteed-- of course, implementations have different strategies to allocate processor time to threads. Therefore, when a timer thread wakes up after its allocated time, the time it actually executes the task depends on JVM's thread scheduling policy and how many other threads are competing for processor time. Therefore, to reduce delays in task execution, you should minimize the number of runnable threads in your application. It is worth considering running the scheduler in a separate JVM to achieve this.
The time JVM takes to perform garbage collection (GC) can be important for large applications that create a large number of objects. By default, when GC occurs, the entire application must wait for it to complete, which may take a few seconds or more. (command line option-the java application launcher for verbose:gc causes each GC event to be reported to the console. To minimize pauses caused by GC, which may hinder immediate task execution, you should minimize the number of objects created by your application Again, this helps to run your scheduling code in a separate JVM. In addition, you can try a variety of optimization options to minimize GC pauses. For example, incremental GC attempts to spread the cost of primary collections over several secondary collections. The cost is that this reduces the efficiency of GC, but it may be an acceptable price for more timely scheduling.
When to call
To determine whether the task is running in a timely manner, it is helpful if the task itself monitors and records any instances of delayed execution. SchedulerTask, such as TimerTask, has a scheduledExecutionTime () method that returns the last time this task was performed. The run () method of evaluating the expression at the beginning of a System.currentTimeMillis ()-scheduledExecutionTime () task allows you to determine the delay time (in milliseconds) for task execution. You can record this value to generate statistics about the distribution of delayed execution. This value can also be used to determine what action a task should take-for example, if the task is too late, it may do nothing. If you follow the above guidelines, your application needs stricter timeliness guarantees.
Thank you for reading! This is the end of the article on "how to arrange repetitive tasks in Java applications". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!
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.