Classes | |
class | CancelableRunnable |
class | TaskWrapper |
Public Types | |
enum | ScheduleDelayMode { FIXED_RATE, FIXED_DELAY } |
Public Member Functions | |
ThreadLoopRunner (Runnable task, long delayTime, TimeUnit unit, ThreadFactory tf, Logger logger) | |
long | getDelayTimeMillis () |
synchronized boolean | setDelayTime (long delayTime, TimeUnit unit) |
ScheduleDelayMode | getDelayMode () |
synchronized void | setDelayMode (ScheduleDelayMode delayMode) |
synchronized void | runLoop () |
synchronized boolean | isLoopRunning () |
boolean | isTaskRunning () |
boolean | isDisabled () |
synchronized void | suspendLoop () |
synchronized boolean | suspendLoopAndWait (long timeout, TimeUnit unit) throws InterruptedException |
synchronized boolean | shutdown (long timeout, TimeUnit unit) throws InterruptedException |
This class allows running some task repeatedly with a given delay. It is similar to the loop in C++ class ACS.Thread.
The code of the task to be executed gets passed to the constructor as a Runnable object. The time between executions is given in the constructor and can later be changed through setDelayTime(long, TimeUnit).
If the task takes some time to execute, you should consider implementing a soft cancel option. To do this, extend CancelableRunnable instead of simply implementing Runnable
. When the action should be canceled (e.g. due to shutdown(long, TimeUnit)), the shouldTerminate
flag will be set. Your code should check this flag at convenient times, and should return if the flag is set. Alternatively you can override the cancel method and terminate the task thread in different ways.
Method runLoop() starts the loop. Optionally the loop can be stopped with suspendLoop() or suspendLoopAndWait(long, TimeUnit), and can also be restarted with another call to runLoop(). To stop the loop for good, call shutdown(long, TimeUnit).
While the underlying classes from the JDK concurrent
package could also be used directly, this class allows for shorter code that is also more similar to the style used in C++. Especially it imposes the limit of running one task repeatedly, which gives an easier API, at the expense of creating a separate instance of ThreadLoopRunner for some other repeated task. (The older JDK class java.util.Timer has problems recovering from errors and should not be used.)
alma.acs.concurrent.ThreadLoopRunner.ThreadLoopRunner | ( | Runnable | task, | |
long | delayTime, | |||
TimeUnit | unit, | |||
ThreadFactory | tf, | |||
Logger | logger | |||
) |
Creates a ThreadLoopRunner
that can repeatedly execute task
. The mode defaults to ScheduleDelayMode#FIXED_RATE unless being changed via setDelayMode(ScheduleDelayMode).
task | user-supplied Runnable, or better subtype ThreadLoopRunner.CancelableRunnable. | |
delayTime | ||
unit | ||
tf | ThreadFactory from which the loop thread will be created. | |
logger | Logger used by this class. |
References alma.acs.concurrent.ThreadLoopRunner.setDelayTime().
ScheduleDelayMode alma.acs.concurrent.ThreadLoopRunner.getDelayMode | ( | ) |
long alma.acs.concurrent.ThreadLoopRunner.getDelayTimeMillis | ( | ) |
boolean alma.acs.concurrent.ThreadLoopRunner.isDisabled | ( | ) |
Returns true
after shutdown(long, TimeUnit) was called. Then invoking any control method of this class will throw an IllegalStateException.
synchronized boolean alma.acs.concurrent.ThreadLoopRunner.isLoopRunning | ( | ) |
true
if the loop is running, regardless of whether the task is currently being executed. Referenced by alma.acs.monitoring.blobber.BlobberImpl.cleanUp(), alma.acs.concurrent.ThreadLoopRunner.runLoop(), alma.acs.concurrent.ThreadLoopRunner.setDelayMode(), and alma.acs.concurrent.ThreadLoopRunner.setDelayTime().
boolean alma.acs.concurrent.ThreadLoopRunner.isTaskRunning | ( | ) |
true
if the task is running, regardless of whether the loop is still running or has been stopped already. Referenced by alma.acs.concurrent.ThreadLoopRunner.runLoop().
synchronized void alma.acs.concurrent.ThreadLoopRunner.runLoop | ( | ) |
Runs the loop, either for the first time, or after a call to suspendLoop().
IllegalStateException | if the loop is already running, or if the run() method of a previous loop is still executing, or after shutdown |
References alma.acs.concurrent.ThreadLoopRunner.isLoopRunning(), and alma.acs.concurrent.ThreadLoopRunner.isTaskRunning().
Referenced by alma.acs.monitoring.blobber.BlobberImpl.execute(), alma.acs.concurrent.ThreadLoopRunner.setDelayTime(), alma.acs.alarmsystem.source.AlarmSourceImpl.start(), and alma.acs.alarmsystem.source.AlarmsMap.start().
synchronized void alma.acs.concurrent.ThreadLoopRunner.setDelayMode | ( | ScheduleDelayMode | delayMode | ) |
Sets the delay mode to be used for the next runLoop().
This method must not be called when the loop is already running (see isLoopRunning()), in which case it throws an IllegalStateException. The reason for this is that we see no need to change this mode on the fly, and rather avoid the overhead of automatically stopping and restarting the loop with the possible complications if the run() method does not terminate. Also we don't want getDelayMode() to give results that are not correct for the currently running loop. Note that the same issue is handled differently in setDelayTime(long, TimeUnit) where it seems desirable to change the delay time while the loop is running.
delayMode | FIXED_RATE or FIXED_DELAY, see ScheduledExecutorService#scheduleAtFixedRate(Runnable, long, long, TimeUnit) and ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit). Note that the C++ implementation uses equivalent of FIXED_RATE. |
IllegalStateException | if called when the loop is running, or after shutdown. |
References alma.acs.concurrent.ThreadLoopRunner.isLoopRunning().
Referenced by alma.acs.monitoring.blobber.BlobberImpl.execute(), alma.acs.alarmsystem.source.AlarmSourceImpl.start(), and alma.acs.alarmsystem.source.AlarmsMap.start().
synchronized boolean alma.acs.concurrent.ThreadLoopRunner.setDelayTime | ( | long | delayTime, | |
TimeUnit | unit | |||
) |
Sets the time between calls to the loop action object, where the time for the task itself is included or not, depending on the set ScheduleDelayMode.
If this method is called while the thread loop is already running, it will wait for the current task to finish and then reschedule the actions at the new rate. If the currently running task fails to finish after 10 seconds, a warning is logged and false
is returned.
Note that it is a limitation in the underlying ScheduledThreadPoolExecutor that the delay time cannot be changed without stopping and restarting the loop. If this becomes a problem, we could use the concurrent lib classes in a more customized way.
delayTime | new delay time | |
unit |
References alma.acs.concurrent.ThreadLoopRunner.isLoopRunning(), alma.acs.concurrent.ThreadLoopRunner.runLoop(), and alma.acs.concurrent.ThreadLoopRunner.suspendLoopAndWait().
Referenced by alma.acs.concurrent.ThreadLoopRunner.ThreadLoopRunner().
synchronized boolean alma.acs.concurrent.ThreadLoopRunner.shutdown | ( | long | timeout, | |
TimeUnit | unit | |||
) | throws InterruptedException |
Shuts down this thread loop runner, attempting to gracefully stop the running task if CancelableRunnable was provided, or otherwise letting the currently running loop action finish.
The ThreadLoopRunner
cannot be used any more after this method has been called. (Then will return true
, other methods will throw IllegalStateException.) The timeout
refers to how long this method waits for the task to terminate. If it terminates before the given timeout, then true
is returned, otherwise false
which means that the Runnable action object is still in use and should not be reused later unless it is re-entrant.
timeout | ||
unit |
InterruptedException | ||
IllegalStateException | if called after shutdown. |
Referenced by alma.acs.monitoring.blobber.BlobberImpl.cleanUp(), alma.acs.alarmsystem.source.AlarmsMap.shutdown(), and alma.acs.alarmsystem.source.AlarmSourceImpl.tearDown().
synchronized void alma.acs.concurrent.ThreadLoopRunner.suspendLoop | ( | ) |
Stops the loop, without attempting to cancel the possibly running action even if it was provided as a CancelableRunnable. Note also that this call returns quickly, without waiting for a possibly running action to finish.
The loop can be started again later via runLoop(). Suspending and restarting the loop does not lead to the creation of a new Thread.
IllegalStateException | if called after shutdown. |
Referenced by alma.acs.concurrent.ThreadLoopRunner.suspendLoopAndWait().
synchronized boolean alma.acs.concurrent.ThreadLoopRunner.suspendLoopAndWait | ( | long | timeout, | |
TimeUnit | unit | |||
) | throws InterruptedException |
Like suspendLoop(), but additionally waits for the currently running task (if any) to finish, with the given timeout applied.
If there is a task running and it fails to terminate, a subsequent call to runLoop() will fail with an IllegalStateException.
timeout | ||
unit |
InterruptedException | if the calling thread is interrupted while waiting for the run method to finish. | |
IllegalStateException | if called after shutdown. |
References alma.acs.concurrent.ThreadLoopRunner.suspendLoop().
Referenced by alma.acs.concurrent.ThreadLoopRunner.setDelayTime().