Class TaskScheduler
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.
Dangers of using this class
At first blink it seems tempting to use this class instead of locks because unlike with locks, methods of this class never block and cannot cause dead-locks unless the submitted tasks wait for each other. While this is true, there are three issues to consider:-
Using
TaskSchedulerhas a higher per task overhead than using a lock. -
Usually there is no telling in which
dispatchTasks()method will a particular task execute. This may add some additional non-determinism, making debugging possibly harder. -
Using locks cause tasks (and the executing threads) to wait for each other,
resulting in a natural throttling.
TaskSchedulerhowever maintains a list of not yet executed tasks and this can lead toOutOfMemoryErrorif 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).
Thread safety
The methods of this class are safe to use by multiple threads concurrently.Synchronization transparency
Other than thedispatchTasks() 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 Summary
ConstructorsConstructorDescriptionTaskScheduler(Executor executor) Creates a new task scheduler (without any task scheduled) with the given backing executor. -
Method Summary
Modifier and TypeMethodDescriptionvoidCalling this method ensures that previously scheduled tasks will be submitted to the executor specified at construction time.booleanChecks whether this method was invoked from adispatchTasks()method call (i.e.: a task is being submitted on the current calling thread).static TaskSchedulerA convenience method effectively equivalent tonew TaskScheduler(SyncTaskExecutor.getSimpleExecutor()).voidscheduleTask(Runnable task) Schedules a single task for submitting to the executor specified at construction time.voidscheduleTasks(List<? extends Runnable> tasks) Schedules a list of tasks for submitting to the executor specified at construction time.toString()Returns the string representation of thisTaskSchedulerin no particular format.
-
Constructor Details
-
TaskScheduler
Creates a new task scheduler (without any task scheduled) with the given backing executor.- Parameters:
executor- the executor to which tasks will be submitted to by thedispatchTasks()method. This argument cannot benull.- Throws:
NullPointerException- thrown if the specified executor isnull
-
-
Method Details
-
newSyncScheduler
A convenience method effectively equivalent tonew TaskScheduler(SyncTaskExecutor.getSimpleExecutor()).- Returns:
- a new task scheduler with a
SyncTaskExecutor.getSimpleExecutor()underlying executor. This method never returnsnulland always returns a new instance.
-
scheduleTask
Schedules a single task for submitting to the executor specified at construction time.This method will not actually submit the task, to do this call the
dispatchTasks()method.- Parameters:
task- the task to be scheduled for submitting to the executor specified at construction time. This argument cannot benull.- Throws:
NullPointerException- thrown if the specified task isnull
-
scheduleTasks
Schedules a list of tasks for submitting to the executor specified at construction time. The tasks will be scheduled in the order defined by the given list.This method will not actually submit the task, to do this call the
dispatchTasks()method.- Parameters:
tasks- the list of tasks to be scheduled for submitting to the executor specified at construction time. This argument cannot benulland cannot containnullelements.- Throws:
NullPointerException- thrown if the specifiedtasksisnullor containsnullelements
-
dispatchTasks
public void dispatchTasks()Calling this method ensures that previously scheduled tasks will be submitted to the executor specified at construction time. Note that tasks may actually be submitted in differentdispatchTasks()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 singledispatchTasks()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)). -
isCurrentThreadDispatching
public boolean isCurrentThreadDispatching()Checks whether this method was invoked from adispatchTasks()method call (i.e.: a task is being submitted on the current calling thread).- Returns:
trueif this method was invoked from adispatchTasks(),falseotherwise
-
toString
Returns the string representation of thisTaskSchedulerin 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.
-