public class ThreadLoopRunner
extends java.lang.Object
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 Timer has problems recovering from errors and should not be used.)
| Modifier and Type | Class and Description |
|---|---|
static class |
ThreadLoopRunner.CancelableRunnable
Variation of
Runnable that allows other threads to give a hint to the
{Runnable.run() method that it should terminate. |
static class |
ThreadLoopRunner.ScheduleDelayMode |
| Constructor and Description |
|---|
ThreadLoopRunner(java.lang.Runnable task,
long delayTime,
java.util.concurrent.TimeUnit unit,
java.util.concurrent.ThreadFactory tf,
java.util.logging.Logger logger,
java.lang.String name)
Creates a
ThreadLoopRunner that can repeatedly execute task. |
| Modifier and Type | Method and Description |
|---|---|
ThreadLoopRunner.ScheduleDelayMode |
getDelayMode() |
long |
getDelayTimeMillis() |
boolean |
isDisabled()
Returns
true after shutdown(long, TimeUnit) was called. |
boolean |
isLoopRunning() |
boolean |
isTaskRunning() |
void |
runLoop()
Runs the loop, either for the first time, or after a call to
suspendLoop(). |
void |
setDelayMode(ThreadLoopRunner.ScheduleDelayMode delayMode)
Sets the delay mode to be used for the next
runLoop(). |
void |
setDelayTime(long delayTime,
java.util.concurrent.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 chosen
ThreadLoopRunner.ScheduleDelayMode. |
boolean |
shutdown(long timeout,
java.util.concurrent.TimeUnit unit)
Shuts down this thread loop runner,
attempting to gracefully stop the running task if
ThreadLoopRunner.CancelableRunnable was provided,
or otherwise letting the currently running loop action finish. |
void |
suspendLoop()
Stops the loop, without attempting to cancel the possibly running action even if it was provided
as a
ThreadLoopRunner.CancelableRunnable. |
boolean |
suspendLoopAndWait(long timeout,
java.util.concurrent.TimeUnit unit)
Like
suspendLoop(), but additionally waits for the currently running task (if any)
to finish, with the given timeout applied. |
public ThreadLoopRunner(java.lang.Runnable task,
long delayTime,
java.util.concurrent.TimeUnit unit,
java.util.concurrent.ThreadFactory tf,
java.util.logging.Logger logger,
java.lang.String name)
ThreadLoopRunner that can repeatedly execute task.
The mode defaults to ThreadLoopRunner.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.name - Facilitates debugging, by using a meaningful name for logs and threads.public long getDelayTimeMillis()
setDelayTime(long, TimeUnit).public void setDelayTime(long delayTime,
java.util.concurrent.TimeUnit unit)
ThreadLoopRunner.ScheduleDelayMode.
If this method is called while the thread loop is already running, the task will be run again right after the currently running task has finished; only after that run we'll get into the proper delay timing.
Implementation note: If this method is called while the thread loop is already running,
it will stop the loop, apply the new value, and then re-start the loop.
It is a limitation in the underlying ScheduledThreadPoolExecutor
that the delay time cannot be changed without stopping and restarting the loop (= repetitive task).
If this becomes a problem, we could use the concurrent lib classes in a more customized way.
delayTime - new delay timeunit - setDelayMode(ScheduleDelayMode)public ThreadLoopRunner.ScheduleDelayMode getDelayMode()
runLoop().public void setDelayMode(ThreadLoopRunner.ScheduleDelayMode delayMode)
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.java.lang.IllegalStateException - if called when the loop is running, or after shutdown.public void runLoop()
suspendLoop().java.lang.IllegalStateException - if the loop is already running,
or if the run() method of a previous loop is still executing,
or after shutdownisLoopRunning()public boolean isLoopRunning()
true if the loop is running, regardless of whether the task is currently being executed.public boolean isTaskRunning()
true if the task is running, regardless of whether the loop is still running or has been stopped already.suspendLoopAndWait(long, TimeUnit)public boolean isDisabled()
true after shutdown(long, TimeUnit) was called.
Then invoking any control method of this class will throw an IllegalStateException.public void suspendLoop()
ThreadLoopRunner.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(), once the running task has finished.
Suspending and restarting the loop does not lead to the creation of a new Thread.
java.lang.IllegalStateException - if called after shutdown.suspendLoopAndWait(long, TimeUnit)public boolean suspendLoopAndWait(long timeout,
java.util.concurrent.TimeUnit unit)
throws java.lang.InterruptedException
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 - java.lang.InterruptedException - if the calling thread is interrupted while waiting for the run method to finish.java.lang.IllegalStateException - if called after shutdown.public boolean shutdown(long timeout,
java.util.concurrent.TimeUnit unit)
throws java.lang.InterruptedException
ThreadLoopRunner.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 isDisabled() 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 - java.lang.InterruptedExceptionjava.lang.IllegalStateException - if called after shutdown.