Class SimpleAsyncTaskScheduler

All Implemented Interfaces:
Serializable, AutoCloseable, Executor, EventListener, Aware, ApplicationContextAware, ApplicationListener<ContextClosedEvent>, Lifecycle, Phased, SmartLifecycle, AsyncListenableTaskExecutor, AsyncTaskExecutor, TaskExecutor, TaskScheduler

A simple implementation of Spring's TaskScheduler interface, using a single scheduler thread and executing every scheduled task in an individual separate thread. This is an attractive choice with virtual threads on JDK 21, expecting common usage with setVirtualThreads(true).

NOTE: Scheduling with a fixed delay enforces execution on the single scheduler thread, in order to provide traditional fixed-delay semantics! Prefer the use of fixed rates or cron triggers instead which are a better fit with this thread-per-task scheduler variant.

Supports a graceful shutdown through SimpleAsyncTaskExecutor.setTaskTerminationTimeout(long), at the expense of task tracking overhead per execution thread at runtime. Supports limiting concurrent threads through SimpleAsyncTaskExecutor.setConcurrencyLimit(int). By default, the number of concurrent task executions is unlimited. This allows for dynamic concurrency of scheduled task executions, in contrast to ThreadPoolTaskScheduler which requires a fixed pool size.

NOTE: This implementation does not reuse threads! Consider a thread-pooling TaskScheduler implementation instead, in particular for scheduling a large number of short-lived tasks. Alternatively, on JDK 21, consider setting SimpleAsyncTaskExecutor.setVirtualThreads(boolean) to true.

Extends SimpleAsyncTaskExecutor and can serve as a fully capable replacement for it, e.g. as a single shared instance serving as a TaskExecutor as well as a TaskScheduler. This is generally not the case with other executor/scheduler implementations which tend to have specific constraints for the scheduler thread pool, requiring a separate thread pool for general executor purposes in practice.

NOTE: This scheduler variant does not track the actual completion of tasks but rather just the hand-off to an execution thread. As a consequence, a ScheduledFuture handle (e.g. from schedule(Runnable, Instant)) represents that hand-off rather than the actual completion of the provided task (or series of repeated tasks).

As an alternative to the built-in thread-per-task capability, this scheduler can also be configured with a separate target executor for scheduled task execution through setTargetTaskExecutor(java.util.concurrent.Executor): e.g. pointing to a shared ThreadPoolTaskExecutor bean. This is still rather different from a ThreadPoolTaskScheduler setup since it always uses a single scheduler thread while dynamically dispatching to the target thread pool which may have a dynamic core/max pool size range, participating in a shared concurrency limit.

Since:
6.1
Author:
Juergen Hoeller
See Also:
  • Constructor Details

    • SimpleAsyncTaskScheduler

      public SimpleAsyncTaskScheduler()
  • Method Details

    • setClock

      public void setClock(Clock clock)
      Set the clock to use for scheduling purposes.

      The default clock is the system clock for the default time zone.

      See Also:
    • getClock

      public Clock getClock()
      Description copied from interface: TaskScheduler
      Return the clock to use for scheduling purposes.
      Specified by:
      getClock in interface TaskScheduler
      See Also:
    • setPhase

      public void setPhase(int phase)
      Specify the lifecycle phase for pausing and resuming this executor. The default is SmartLifecycle.DEFAULT_PHASE.
      See Also:
    • getPhase

      public int getPhase()
      Return the lifecycle phase for pausing and resuming this executor.
      Specified by:
      getPhase in interface Phased
      Specified by:
      getPhase in interface SmartLifecycle
      See Also:
    • setTargetTaskExecutor

      public void setTargetTaskExecutor(Executor targetTaskExecutor)
      Specify a custom target Executor to delegate to for the individual execution of scheduled tasks. This can for example be set to a separate thread pool for executing scheduled tasks, whereas this scheduler keeps using its single scheduler thread.

      If not set, the regular SimpleAsyncTaskExecutor arrangements kicks in with a new thread per task.

    • setApplicationContext

      public void setApplicationContext(ApplicationContext applicationContext)
      Description copied from interface: ApplicationContextAware
      Set the ApplicationContext that this object runs in. Normally this call will be used to initialize the object.

      Invoked after population of normal bean properties but before an init callback such as InitializingBean.afterPropertiesSet() or a custom init-method. Invoked after ResourceLoaderAware.setResourceLoader(org.springframework.core.io.ResourceLoader), ApplicationEventPublisherAware.setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher) and MessageSourceAware, if applicable.

      Specified by:
      setApplicationContext in interface ApplicationContextAware
      Parameters:
      applicationContext - the ApplicationContext object to be used by this object
      See Also:
    • doExecute

      protected void doExecute(Runnable task)
      Description copied from class: SimpleAsyncTaskExecutor
      Template method for the actual execution of a task.

      The default implementation creates a new Thread and starts it.

      Overrides:
      doExecute in class SimpleAsyncTaskExecutor
      Parameters:
      task - the Runnable to execute
      See Also:
    • schedule

      @Nullable public ScheduledFuture<?> schedule(Runnable task, Trigger trigger)
      Description copied from interface: TaskScheduler
      Schedule the given Runnable, invoking it whenever the trigger indicates a next execution time.

      Execution will end once the scheduler shuts down or the returned ScheduledFuture gets cancelled.

      Specified by:
      schedule in interface TaskScheduler
      Parameters:
      task - the Runnable to execute whenever the trigger fires
      trigger - an implementation of the Trigger interface, e.g. a CronTrigger object wrapping a cron expression
      Returns:
      a ScheduledFuture representing pending execution of the task, or null if the given Trigger object never fires (i.e. returns null from Trigger.nextExecution(org.springframework.scheduling.TriggerContext))
      See Also:
    • schedule

      public ScheduledFuture<?> schedule(Runnable task, Instant startTime)
      Description copied from interface: TaskScheduler
      Schedule the given Runnable, invoking it at the specified execution time.

      Execution will end once the scheduler shuts down or the returned ScheduledFuture gets cancelled.

      Specified by:
      schedule in interface TaskScheduler
      Parameters:
      task - the Runnable to execute whenever the trigger fires
      startTime - the desired execution time for the task (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
      Returns:
      a ScheduledFuture representing pending execution of the task
    • scheduleAtFixedRate

      public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period)
      Description copied from interface: TaskScheduler
      Schedule the given Runnable, invoking it at the specified execution time and subsequently with the given period.

      Execution will end once the scheduler shuts down or the returned ScheduledFuture gets cancelled.

      Specified by:
      scheduleAtFixedRate in interface TaskScheduler
      Parameters:
      task - the Runnable to execute whenever the trigger fires
      startTime - the desired first execution time for the task (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
      period - the interval between successive executions of the task
      Returns:
      a ScheduledFuture representing pending execution of the task
    • scheduleAtFixedRate

      public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period)
      Description copied from interface: TaskScheduler
      Schedule the given Runnable, starting as soon as possible and invoking it with the given period.

      Execution will end once the scheduler shuts down or the returned ScheduledFuture gets cancelled.

      Specified by:
      scheduleAtFixedRate in interface TaskScheduler
      Parameters:
      task - the Runnable to execute whenever the trigger fires
      period - the interval between successive executions of the task
      Returns:
      a ScheduledFuture representing pending execution of the task
    • scheduleWithFixedDelay

      public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay)
      Description copied from interface: TaskScheduler
      Schedule the given Runnable, invoking it at the specified execution time and subsequently with the given delay between the completion of one execution and the start of the next.

      Execution will end once the scheduler shuts down or the returned ScheduledFuture gets cancelled.

      Specified by:
      scheduleWithFixedDelay in interface TaskScheduler
      Parameters:
      task - the Runnable to execute whenever the trigger fires
      startTime - the desired first execution time for the task (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
      delay - the delay between the completion of one execution and the start of the next
      Returns:
      a ScheduledFuture representing pending execution of the task
    • scheduleWithFixedDelay

      public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay)
      Description copied from interface: TaskScheduler
      Schedule the given Runnable, starting as soon as possible and invoking it with the given delay between the completion of one execution and the start of the next.

      Execution will end once the scheduler shuts down or the returned ScheduledFuture gets cancelled.

      Specified by:
      scheduleWithFixedDelay in interface TaskScheduler
      Parameters:
      task - the Runnable to execute whenever the trigger fires
      delay - the delay between the completion of one execution and the start of the next
      Returns:
      a ScheduledFuture representing pending execution of the task
    • start

      public void start()
      Description copied from interface: Lifecycle
      Start this component.

      Should not throw an exception if the component is already running.

      In the case of a container, this will propagate the start signal to all components that apply.

      Specified by:
      start in interface Lifecycle
      See Also:
    • stop

      public void stop()
      Description copied from interface: Lifecycle
      Stop this component, typically in a synchronous fashion, such that the component is fully stopped upon return of this method. Consider implementing SmartLifecycle and its stop(Runnable) variant when asynchronous stop behavior is necessary.

      Note that this stop notification is not guaranteed to come before destruction: On regular shutdown, Lifecycle beans will first receive a stop notification before the general destruction callbacks are being propagated; however, on hot refresh during a context's lifetime or on aborted refresh attempts, a given bean's destroy method will be called without any consideration of stop signals upfront.

      Should not throw an exception if the component is not running (not started yet).

      In the case of a container, this will propagate the stop signal to all components that apply.

      Specified by:
      stop in interface Lifecycle
      See Also:
    • stop

      public void stop(Runnable callback)
      Description copied from interface: SmartLifecycle
      Indicates that a Lifecycle component must stop if it is currently running.

      The provided callback is used by the LifecycleProcessor to support an ordered, and potentially concurrent, shutdown of all components having a common shutdown order value. The callback must be executed after the SmartLifecycle component does indeed stop.

      The LifecycleProcessor will call only this variant of the stop method; i.e. Lifecycle.stop() will not be called for SmartLifecycle implementations unless explicitly delegated to within the implementation of this method.

      The default implementation delegates to Lifecycle.stop() and immediately triggers the given callback in the calling thread. Note that there is no synchronization between the two, so custom implementations may at least want to put the same steps within their common lifecycle monitor (if any).

      Specified by:
      stop in interface SmartLifecycle
      See Also:
    • isRunning

      public boolean isRunning()
      Description copied from interface: Lifecycle
      Check whether this component is currently running.

      In the case of a container, this will return true only if all components that apply are currently running.

      Specified by:
      isRunning in interface Lifecycle
      Returns:
      whether the component is currently running
    • onApplicationEvent

      public void onApplicationEvent(ContextClosedEvent event)
      Description copied from interface: ApplicationListener
      Handle an application event.
      Specified by:
      onApplicationEvent in interface ApplicationListener<ContextClosedEvent>
      Parameters:
      event - the event to respond to
    • close

      public void close()
      Description copied from class: SimpleAsyncTaskExecutor
      This close methods tracks the termination of active threads if a concrete task termination timeout has been set. Otherwise, it is not necessary to close this executor.
      Specified by:
      close in interface AutoCloseable
      Overrides:
      close in class SimpleAsyncTaskExecutor