Implementing the Plan Framework in the previous section, we have learned how to use the plan frame and compare it with the Java timer framework. Below, I will show you how to implement this framework. In addition to the Scheduleiterator interface shown in Listing 3, there are two other classes of this framework - Scheduler and Schedulertask. These classes actually use Timer and Schedulertask internally because plan is actually a series of single timers.
Listing 5 and 6 show these two categories of source code: 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 itrator) {
This.Schedulertask = schedulertask;
This.iterator = Iterator;
}
Public void run () {
Schedulertask.run ();
Reschadule (Schedulertask, Iterator);
}
}
PRIVATE FINAL TIMER = New Timer ();
Public scheduler () {
}
Public void ca Zancel () {
Timer.cancel ();
}
Public void schedule (Schedulertask Schedulertask,
Scheduleiterator itrator) {
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 ca Zanelled ");
}
Schedulertask.State = schedulertask.scheduled;
Schedulertask.timertask =
New SchedulertImertask (Schedulertask, Iterator);
Timer.schedule (Schedulertask.Timertask, Time);
}
}
}
Private void reschedule (Schedulertask Schedulertask,
Scheduleiterator itrator) {
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:
Package 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 firm 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 = Cancelle;
Return Result;
}
}
Public long scheduledexecutionTime () {
Synchronized (LOCK) {
Return Timertask == NULL? 0: Timertask.scheduledexecutionTime ();
}
}
}
Just like the egg timer, each instance of Scheduler has an instance of Timer for providing the underlying plan. Scheduler did not use a single timer like a cooking egg timer, and it took a set of single timer strings to perform the Schedulertask class in each time specified by ScheduleITOR.
Consider the Public Schedule () method on Scheduler - this is a planned entry point because it is a method called by the customer (in the Cancel Task section will describe only another public method Cancel ()). By calling next () of the Scheduleiterator interface, it is found that the time to perform Schedulertask first. Then start the plan at this time by calling a single Schedule () method of the underlying Timer class. The Timertask object provided for a single execution is an instance of the embedded SchedulertImertask class, which is packaged in tasks and iterator. At the specified time, the Run () method of the embedded class is called, which uses the package task and iterator reference to re-plan the next execution of the task. The reschedule () method is very similar to the Schedule () method, but it is private and executes a slightly different SCHEDULERTASK status check. The replay process repeatedly repeatedly, and constructs a new embedded instance for each plan until the task or scheduler is canceled (or JVM is turned off).
Similar to Timertask, Schedulertask has experienced a series of states in its life cycle. After creating, it is in a virgin state, which indicates that it has never planned. After the plan, it changed to the Scheduled state, and then cancel the task with one of the methods described below, it changes to the Cancelled state. Manage the correct status transformation - If you guarantee that you don't have two plans for a non-VIRGIN state - Added the complexity of the Scheduler and the Schedulertask class. When performing operations that may change the task status, the code must synchronize the lock object of the task. Cancel the task
There are three ways to cancel the plan task. The first is the Cancel () method that calls Schedulertask. This is like calling Timertask's Cancel () method: The task will never run, but the task that has been run will still run. The return value of the Cancel () method is a Boolean value, indicating whether the scheduled task will run if no Cancel () is called. More specifically, if the task is called Cancel () before calling Cancel (), then it returns True. If you try to plan a canceled (even planned) task again, SCHEDULER will throw an ILLEGALSTATEEXCEPTION.
The second way to cancel the planning task is to let ScheduleITOR returns NULL. This is just a simplified operation of the first way, because the Scheduler class calls the Cancel () method of the Schedulertask class. If you want to use an iterator instead of a task to control the plan stop time, you use this way to cancel the task.
The third way is to cancel the entire Scheduler by calling its Cancel () method. This will cancel all the tasks of the debugger and make it programs any tasks.
Extending the CRON utility
The plan frame can be used as a UNIX utility, but the specification of the plan is mandatory rather than declaration. For example, in the ALARMCLOCK implementation, the DAILYITERATOR class used, its plan is the same as the cron job plan, which is specified by the crontab item starting with 0 7 * * (these fields respectively specify minutes, hour, month, and weeks. ).
However, the plan frame is more flexible than CRON. Imagine a HeatingController app that opens hot water in the morning. I want to indicate it "Open hot water at 8:00 am every working day, and open hot water at 9:00 am." With cron, I need two crontab items (0 8 * 1, 2, 3, 4, 5, and 0 9 * * 6, 7). Use the SCHEDULEITERATOR solution is more concise because I can use the composition to define a single antennament. Listing 7 shows one of the methods:
Listing 7. Define a single iterator with a compound
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 compositeiteiterator
New scheduleiterator [] {
New RestrictedDailyIterator (8, 0, 0, weekdays),
New RestrictedDailyItemrator (9, 0, 0, weekend)}
);
The RestrictedDailyTeiterator class is very similar to DailyIterator, but it limits to run in a certain day of the week, and a CompositeTeiterator class gets a set of ScheduleITORS and ranks the date to a single plan.
There are many plans to be CRON unable to generate, but the ScheduleITOR is achievable. For example, the plan described in the last day of the month can be implemented with a standard Java calendar algorithm (with Calendar class), and it cannot be expressed in cron. Applications do not need to use the Calendar class. In the source code of this article (see Resources), I joined an example of a security light controller, which is running according to "15 minutes before sunset". This implementation uses Calendrical Calculation Software Package to calculate the sunset time of local (given longitude and latitude).
Real-time guarantee
Be sure to know what the framework has a guarantee when writing a planned application. Is my mission to execute in advance or latency? If there is an advance or delay, how much is the maximum deviation? Unfortunately, there is no simple answer to these issues. However, in practice, its behavior is enough for many applications. The following discussion assumes that the system clock is correct.
Because SCHEDULER will plan to delegate the Timer class, Scheduler can make a real-time guarantee as Timer. Timer Plan with Object.wait (long) method. The current thread should wait until it wakes it, waking up may be one of the following reasons:
1. Another thread calls the NOTIFY () or NotifyAll () method of the object.
2. The thread is interrupted by another thread.
3. In the absence of notification, thread is awakened (called Spurious Wakeup, Joshua Bloch Effective Java Programming Language Guide) describes it.
4. The specified time has arrived.
For Timer classes, the first possibility does not occur because the objects that call Wait () are private. Even so, the Timer implementation is still protected for the top three advanced wake-up, so that the thread wakes up after a specified time. Currently, Object.wait (long) document annotation declares, it will wake up before and after the specified time, so threads may wake up in advance. In this example, Timer will make another wait () execute (ScheduledExecutionTime - System.currentTimeMillis ()) in milliseconds, ensuring that the task will never be executed in advance. Will the task be delayed? Will do. There are two main reasons for delays: thread plans and garbage collection.
Java language norms deliberately do not have strict regulations for threads. This is because the Java platform is universal and for large-scale hardware and its related operating systems. Although most JVM implementation has fair thread schedulers, this is not guaranteed - of course, each implementation has different strategies for the processor time for threads. Therefore, when the TIMER thread wakes up after the assignment time, it actually executes its task depends on the JVM thread plan policy, and how many other threads competition processor time. Therefore, to slow down the delay execution of the task, you should minimize the number of threads running in the application. In order to do this, consider run the scheduler in a separate JVM. For large applications for creating a large number of objects, JVM spent on garbage collection (GC) will have a lot. By default, when GC is performed, the entire application must wait for it, this may have a few seconds or even longer (the command line option of the Java application launch) -verbose: GC will result in reporting to the console A GC event). To minimize these pauses caused by GC (this may affect the execution of the quick task), the number of objects created by the application should be minimized. Again, run the plan code in a separate JVM is helpful. At the same time, several fine-tuning options can be tried to reduce GC pauses as much as possible. For example, incremental GCs will try to disperse the cost of the main collection to several small collection. Of course, this will reduce the efficiency of GC, but this may be an acceptable price of the time plan.
When is it planned?
If the task itself can monitor and record the instance of all delays, it is helpful for determining whether the task can run on time. Schedulertask is similar to Timertask, there is a ScheduledExecutionTime () method that returns the time that the planned task is recently executed. At the beginning of the RUN () method of the task, the expression system.currenttime () - ScheduledExecutionTime () is determined, allowing you to determine how long the task is delayed (in milliseconds). This value can be recorded in order to generate a distribution statistics for delay execution. You can use this value to determine what action should be taken - for example, if the task is too late, then it may not do anything. In the case of following the above principles, if the application needs more stringent time guarantees, refer to Java's real-time specification.
Conclude
In this article, I introduced a simple enhancement of the Java timer framework, which makes flexible planning strategies possible. The new frame is essentially more common CRON - in fact, the cron is implemented as a Scheduleiterator interface to replace simple Java Cron, which is very useful. Although there is no strict real-time guarantee, many of the general Java applications that need to be planned regular tasks can be used to use this frame.
Reference
"
Tuning Garbage Collection with the 1.3.1 Java Virtual Machine "is a very useful article of Sun, which gives a prompt on how to minimize GC pause time.
· To get more information about GC in DeveloperWorks, see the following article:
"
Java Theory and Practice: Simply Collection of Garbage "(October 2003).
"
Mash That Trash "(July 2003).
"
Fine-Tuning Java Garbage Collection Performance "(Jan 2003)."
SENSIBLE SANITATION, PART 1 "(August 2002).
"
SENSIBLE SANITATION, PART 2 "(Aug 2002).
"
SENSIBLE SANITATION, PART 3 "(Sep 2002).
·in"
Java Theory and Practice: Passing to a certain extent makes everything makes everything "(November 2002), Brian Goetz discussed the Doug LEA's Util.Concurrent library, which is a treasure gallery of concurrent utility classes.
· Another article of Brian Goetz "
Threading Lightly, Part 2: Reducing Contention (September 2001) Analyzed thread bidding and how to reduce it.