public final class TaskScheduler extends Object
scheduled then be
executed later by an Executor or
TaskExecutor. The tasks will be submitted to the executor in the
order they were scheduled.
The main benefit of using this class is that scheduling a task is synchronization transparent and therefore can be called while a lock is held. Since submitting task happens in the same order as the scheduling this can be used to invoke event handlers because with events it is usually important to notify the listeners in the order the events actually occurred.
It is important to note that to achieve the aforementioned goals tasks will never be submitted concurrently, they will be executed one after another. An additional useful side-effect is that tasks will not even be submitted while another task is being executed on the current thread avoiding another possible source of problems. To clarify this, see the following example:
void doPrint() {
final TaskScheduler scheduler;
scheduler = new TaskScheduler(SyncTaskExecutor.getSimpleExecutor());
scheduler.scheduleTask(() -> {
System.out.print("2");
scheduler.scheduleTask(() -> System.out.print("4"));
scheduler.dispatchTasks(); // In this case, this is a no-op
System.out.print("3");
});
System.out.print("1");
// The next method call will execute both the above scheduled tasks.
scheduler.dispatchTasks();
System.out.print("5");
}
The above doPrint() method will always print "12345" and the
scheduler.dispatchTasks() call in the scheduled task will actually
do nothing in this case but return immediately.
The behaviour of this class can also be exploited to "synchronize" actions
without locking, so even tasks not being synchronization transparent
and also not thread-safe can be used safely using this class. This is the
feature what the TaskExecutors.inOrderExecutor(TaskExecutor) and
TaskExecutors.inOrderSyncExecutor() classes provide.
TaskScheduler has a higher per task overhead than using a
lock.
dispatchTasks() method will
a particular task execute. This may add some additional non-determinism,
making debugging possibly harder.
TaskScheduler however maintains
a list of not yet executed tasks and this can lead to
OutOfMemoryError if tasks are scheduled faster than can be
submitted to the executor by a single thread. Notice that with locks
the number of such tasks waiting to be executed is limited by the number
of threads (which should not be too high).
dispatchTasks() method, methods of
this class are synchronization transparent. The
dispatchTasks() method is not synchronization transparent
only because it submits tasks to the underlying Executor. If
submitting tasks to the underlying executor is
synchronization transparent then even this method is
synchronization transparent.| Constructor and Description |
|---|
TaskScheduler(Executor executor)
Creates a new task scheduler (without any task scheduled) with the given
backing executor.
|
| Modifier and Type | Method and Description |
|---|---|
void |
dispatchTasks()
Calling this method ensures that previously scheduled tasks will be
submitted to the executor specified at construction time.
|
boolean |
isCurrentThreadDispatching()
Checks whether this method was invoked from a
dispatchTasks() method call (i.e.: a task is
being submitted on the current calling thread). |
static TaskScheduler |
newSyncScheduler()
A convenience method effectively equivalent to
new TaskScheduler(SyncTaskExecutor.getSimpleExecutor()). |
void |
scheduleTask(Runnable task)
Schedules a single task for submitting to the executor specified at
construction time.
|
void |
scheduleTasks(List<? extends Runnable> tasks)
Schedules a list of tasks for submitting to the executor specified at
construction time.
|
String |
toString()
Returns the string representation of this
TaskScheduler in no
particular format. |
public TaskScheduler(Executor executor)
executor - the executor to which tasks will be submitted to by the
dispatchTasks() method. This argument cannot
be null.NullPointerException - thrown if the specified executor is
nullpublic static TaskScheduler newSyncScheduler()
new TaskScheduler(SyncTaskExecutor.getSimpleExecutor()).SyncTaskExecutor.getSimpleExecutor() underlying executor. This
method never returns null and always returns a new instance.public void scheduleTask(Runnable task)
This method will not actually submit the task, to
do this call the dispatchTasks() method.
task - the task to be scheduled for submitting to the executor
specified at construction time. This argument cannot be null.NullPointerException - thrown if the specified task is nullpublic void scheduleTasks(List<? extends Runnable> tasks)
This method will not actually submit the task, to
do this call the dispatchTasks() method.
tasks - the list of tasks to be scheduled for submitting to the
executor specified at construction time. This argument cannot be
null and cannot contain null elements.NullPointerException - thrown if the specified tasks is
null or contains null elementspublic void dispatchTasks()
dispatchTasks() method
call but calling this method ensures that they will be submitted.
In case submitting a task causes an exception to be thrown, the
dispatchTasks() method actually submitting that task will
propagate that exception to the caller. Note however that a single
dispatchTasks() call can submit multiple tasks and if more than
one throws an exception, the exceptions after the first one will be
suppressed (See: Throwable.addSuppressed(Throwable)).
public boolean isCurrentThreadDispatching()
dispatchTasks() method call (i.e.: a task is
being submitted on the current calling thread).true if this method was invoked from a
dispatchTasks(), false otherwisepublic String toString()
TaskScheduler in no
particular format. The string representation will contain the number of
tasks current waiting to be submitted.
This method is intended to be used for debugging only.