Class TaskScheduler

java.lang.Object
org.jtrim2.executor.TaskScheduler

public final class TaskScheduler extends Object
Allows tasks to be 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 TaskScheduler has 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. 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).
Therefore only use this class when locks are not an option. That is, favor locks when they are safe to use. One of the good use of this class is to notify event listeners.

Thread safety

The methods of this class are safe to use by multiple threads concurrently.

Synchronization transparency

Other than the 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.
See Also:
  • Constructor Summary

    Constructors
    Constructor
    Description
    Creates a new task scheduler (without any task scheduled) with the given backing executor.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Calling this method ensures that previously scheduled tasks will be submitted to the executor specified at construction time.
    boolean
    Checks whether this method was invoked from a dispatchTasks() method call (i.e.: a task is being submitted on the current calling thread).
    A convenience method effectively equivalent to new TaskScheduler(SyncTaskExecutor.getSimpleExecutor()).
    void
    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.
    Returns the string representation of this TaskScheduler in no particular format.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • Constructor Details

    • TaskScheduler

      public TaskScheduler(Executor executor)
      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 the dispatchTasks() method. This argument cannot be null.
      Throws:
      NullPointerException - thrown if the specified executor is null
  • Method Details

    • newSyncScheduler

      public static TaskScheduler newSyncScheduler()
      A convenience method effectively equivalent to new TaskScheduler(SyncTaskExecutor.getSimpleExecutor()).
      Returns:
      a new task scheduler with a SyncTaskExecutor.getSimpleExecutor() underlying executor. This method never returns null and always returns a new instance.
    • scheduleTask

      public void scheduleTask(Runnable task)
      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 be null.
      Throws:
      NullPointerException - thrown if the specified task is null
    • scheduleTasks

      public void scheduleTasks(List<? extends Runnable> tasks)
      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 be null and cannot contain null elements.
      Throws:
      NullPointerException - thrown if the specified tasks is null or contains null elements
    • 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 different 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)).

    • isCurrentThreadDispatching

      public 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).
      Returns:
      true if this method was invoked from a dispatchTasks(), false otherwise
    • toString

      public String toString()
      Returns the string representation of this 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.

      Overrides:
      toString in class Object
      Returns:
      the string representation of this object in no particular format. This method never returns null.