Close

Spring - Task Scheduling

[Updated: Feb 25, 2017, Created: Dec 19, 2016]

Task scheduling is the process of creating tasks to run in future time. The task can be for just one time execution or it can be repeated with a specified frequency.



TaskScheduler interface

Spring scheduling API is based on TaskScheduler.


package org.springframework.scheduling;

import java.util.Date;
import java.util.concurrent.ScheduledFuture;

public interface TaskScheduler {

	ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
	ScheduledFuture<?> schedule(Runnable task, Date startTime);
	ScheduledFuture<?> scheduleAtFixedRate(Runnable task,
                                         Date startTime, long period);
	ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
	ScheduledFuture<?> scheduleWithFixedDelay(Runnable task,
                                         Date startTime, long delay);
	ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
}

Above interface looks similar to JSE ScheduledExecutorService. Additionally Spring unifies JSE (JSR-166), JEE (JSR-236) and third party scheduling API into a single TaskScheduler interface.

Trigger interface is based on the same concept as provided by javax.enterprise.concurrent.Trigger. Trigger determines the next execution time based on past execution outcomes or it can be based on any arbitrary conditions.




TaskScheduler implementations


ThreadPoolTaskScheduler


java.lang.ObjectObjectorg.springframework.util.CustomizableThreadCreatorCustomizableThreadCreatorjava.io.SerializableSerializableorg.springframework.scheduling.concurrent.CustomizableThreadFactoryCustomizableThreadFactoryjava.util.concurrent.ThreadFactoryThreadFactoryorg.springframework.scheduling.concurrent.ExecutorConfigurationSupportExecutorConfigurationSupportorg.springframework.beans.factory.BeanNameAwareBeanNameAwareorg.springframework.beans.factory.InitializingBeanInitializingBeanorg.springframework.beans.factory.DisposableBeanDisposableBeanorg.springframework.scheduling.concurrent.ThreadPoolTaskSchedulerThreadPoolTaskSchedulerorg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutororg.springframework.scheduling.SchedulingTaskExecutorSchedulingTaskExecutororg.springframework.scheduling.TaskSchedulerTaskSchedulerLogicBig

ThreadPoolTaskScheduler is a wrapper around JSE ScheduledThreadPoolExecutor.

Following example shows how ThreadPoolTaskScheduler can be used to schedule tasks at fixed rate:

package com.logicbig.example;


import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.time.LocalTime;

public class ThreadPoolTaskSchedulerExample {
    public static void main (String[] args) throws InterruptedException {
        ThreadPoolTaskScheduler s = new ThreadPoolTaskScheduler();
        s.setPoolSize(5);
        s.initialize();
        for (int i = 0; i < 2; i++) {
            s.scheduleAtFixedRate(
                      () -> System.out.printf("Task: %s, Time: %s:%n",
                                              Thread.currentThread().getName(),
                                              LocalTime.now()), 1000);
        }
        Thread.sleep(10000);
        System.out.println("shutting down after 10 sec");
        s.getScheduledThreadPoolExecutor().shutdownNow();

    }
}

Output

Task: ThreadPoolTaskScheduler-2, Time: 13:17:11.555:
Task: ThreadPoolTaskScheduler-1, Time: 13:17:11.555:
Task: ThreadPoolTaskScheduler-2, Time: 13:17:12.548:
Task: ThreadPoolTaskScheduler-1, Time: 13:17:12.548:
Task: ThreadPoolTaskScheduler-3, Time: 13:17:13.547:
Task: ThreadPoolTaskScheduler-4, Time: 13:17:13.547:
Task: ThreadPoolTaskScheduler-3, Time: 13:17:14.548:
Task: ThreadPoolTaskScheduler-1, Time: 13:17:14.548:
Task: ThreadPoolTaskScheduler-3, Time: 13:17:15.547:
Task: ThreadPoolTaskScheduler-2, Time: 13:17:15.547:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:16.547:
Task: ThreadPoolTaskScheduler-3, Time: 13:17:16.547:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:17.547:
Task: ThreadPoolTaskScheduler-3, Time: 13:17:17.547:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:18.548:
Task: ThreadPoolTaskScheduler-1, Time: 13:17:18.548:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:19.547:
Task: ThreadPoolTaskScheduler-2, Time: 13:17:19.547:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:20.547:
Task: ThreadPoolTaskScheduler-4, Time: 13:17:20.547:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:21.547:
Task: ThreadPoolTaskScheduler-5, Time: 13:17:21.547:
shutting down after 10 sec


Using Trigger

Spring provides two implementations of Trigger: PeriodicTrigger and CronTrigger


PeriodicTrigger example

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.PeriodicTrigger;

import java.time.LocalTime;
import java.util.concurrent.TimeUnit;

public class PeriodicTriggerExample {
    public static void main (String[] args) throws InterruptedException {
        ThreadPoolTaskScheduler s = new ThreadPoolTaskScheduler();
        s.setPoolSize(5);
        s.initialize();
        for (int i = 0; i < 2; i++) {
            s.schedule(getTask(), new PeriodicTrigger(5, TimeUnit.SECONDS));
        }
        Thread.sleep(10000);
        System.out.println("shutting down after 10 sec");
        s.getScheduledThreadPoolExecutor().shutdownNow();
    }

    public static Runnable getTask () {
        return () -> System.out.printf("Task: %s, Time: %s:%n",
                                       Thread.currentThread().getName(),
                                       LocalTime.now());
    }
}

Output

Dec 20, 2016 9:33:28 PM org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler initialize
INFO: Initializing ExecutorService
Task: ThreadPoolTaskScheduler-1, Time: 21:33:28.501:
Task: ThreadPoolTaskScheduler-2, Time: 21:33:28.501:
Task: ThreadPoolTaskScheduler-1, Time: 21:33:33.509:
Task: ThreadPoolTaskScheduler-2, Time: 21:33:33.509:
shutting down after 10 sec

CronTrigger example

Cron, generally, is a utility to create time based expressions particularly used to schedule tasks.

The following example demonstrate how to use CronTrigger to schedule a task which will execute everyday at 22:45 (10:45 pm).

package com.logicbig.example;

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

import java.time.LocalTime;

public class CronTriggerExample {
    public static void main (String[] args) throws InterruptedException {
        System.out.println("Program starts at : " + LocalTime.now());
        ThreadPoolTaskScheduler s = new ThreadPoolTaskScheduler();
        s.setPoolSize(5);
        s.initialize();
        for (int i = 0; i < 2; i++) {
            s.schedule(getTask(), new CronTrigger("0 45 22 * * ?"));
        }
        s.getScheduledThreadPoolExecutor().shutdown();
    }

    public static Runnable getTask () {
        return () -> System.out.printf("Task: %s, Time: %s:%n",
                                       Thread.currentThread().getName(),
                                       LocalTime.now());
    }
}

Output

Program starts at : 22:32:24.630
Task: ThreadPoolTaskScheduler-1, Time: 22:45:00.015:
Task: ThreadPoolTaskScheduler-2, Time: 22:45:00.015:



ConcurrentTaskScheduler

java.lang.ObjectObjectorg.springframework.scheduling.concurrent.ConcurrentTaskExecutorConcurrentTaskExecutororg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutororg.springframework.scheduling.SchedulingTaskExecutorSchedulingTaskExecutororg.springframework.scheduling.concurrent.ConcurrentTaskSchedulerConcurrentTaskSchedulerorg.springframework.scheduling.TaskSchedulerTaskSchedulerLogicBig

This implementation is an adapter for the provided JSE ScheduledExecutorService object.

As this class extends ConcurrentTaskExecutor we can optionally set a decorator around underlying executor tasks.

Example:

public class ConcurrentTaskSchedulerExample {

    public static void main (String[] args) throws InterruptedException {
        ConcurrentTaskScheduler s = new ConcurrentTaskScheduler(
                  Executors.newScheduledThreadPool(5));
        for (int i = 0; i out 10; i++) {
            final int finalI = i;
            s.schedule(() -> {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf("task: %d. Thread: %s%n",
                                  finalI,
                                  Thread.currentThread().getName());
            }, new PeriodicTrigger(1000));
        }
        //shutting down after 3 sec
        Thread.sleep(3000);
        System.out.println("--- shutting down ----");
        ((ExecutorService) s.getConcurrentExecutor()).shutdown();
    }
}

Output

task: 3. Thread: pool-1-thread-4
task: 1. Thread: pool-1-thread-2
task: 0. Thread: pool-1-thread-1
task: 2. Thread: pool-1-thread-3
task: 4. Thread: pool-1-thread-5
task: 5. Thread: pool-1-thread-4
task: 8. Thread: pool-1-thread-3
task: 7. Thread: pool-1-thread-1
task: 6. Thread: pool-1-thread-2
task: 9. Thread: pool-1-thread-5
task: 3. Thread: pool-1-thread-4
task: 1. Thread: pool-1-thread-3
task: 0. Thread: pool-1-thread-1
task: 2. Thread: pool-1-thread-2
task: 4. Thread: pool-1-thread-5
task: 6. Thread: pool-1-thread-2
task: 7. Thread: pool-1-thread-1
task: 8. Thread: pool-1-thread-3
task: 5. Thread: pool-1-thread-4
task: 9. Thread: pool-1-thread-5
--- shutting down ----
task: 0. Thread: pool-1-thread-5
task: 1. Thread: pool-1-thread-3
task: 4. Thread: pool-1-thread-4
task: 2. Thread: pool-1-thread-1
task: 3. Thread: pool-1-thread-2
task: 8. Thread: pool-1-thread-4
task: 6. Thread: pool-1-thread-5
task: 5. Thread: pool-1-thread-1
task: 7. Thread: pool-1-thread-3
task: 9. Thread: pool-1-thread-2



DefaultManagedTaskScheduler

java.lang.ObjectObjectorg.springframework.scheduling.concurrent.ConcurrentTaskExecutorConcurrentTaskExecutororg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutororg.springframework.scheduling.SchedulingTaskExecutorSchedulingTaskExecutororg.springframework.scheduling.concurrent.ConcurrentTaskSchedulerConcurrentTaskSchedulerorg.springframework.scheduling.TaskSchedulerTaskSchedulerorg.springframework.scheduling.concurrent.DefaultManagedTaskSchedulerDefaultManagedTaskSchedulerorg.springframework.beans.factory.InitializingBeanInitializingBeanLogicBig

This implementation implicitly initializes the underlying task scheduler via JNDI lookup.

According to JSR-236 specification: The Java EE product provider (the application server) must bind a pre-configured, default ManagedScheduledExecutorService so that application components can find it under the JNDI name java:comp/DefaultManagedScheduledExecutorService.

Spring's DefaultManagedTaskScheduler internally initializes the underlying scheduler from this JNDI look up. The scheduler must be used as a bean otherwise this class will work as super class ConcurrentTaskScheduler.

Also this class is not strictly JSR-236 based; it can work with any regular ScheduledExecutorService that can be found in JNDI.

In the following example we are not going to demonstrate implicit JNDI binding in a JEE server environment but instead we are going to bind "java:comp/DefaultManagedScheduledExecutorService" manually by using SimpleNamingContextBuilder.

@Configuration
public class MyConfig {

    @Bean
    TaskScheduler taskScheduler () {
        return new DefaultManagedTaskScheduler();
    }

    @Bean
    MyBean myBean () {
        return new MyBean();
    }
}
public class MyBean {
    @Autowired
    TaskScheduler taskScheduler;

    public void runTask () {
        taskScheduler.scheduleWithFixedDelay((Runnable) () -> {
            System.out.println("running " + LocalTime.now());
        }, 1000L);
    }
}
public class DefaultManagedTaskSchedulerExample {

  public static void main (String[] args) throws NamingException, InterruptedException {
      //binding the scheduler manually,
      // In JEE compliant server environment this will be
      // provided by the server product

      SimpleNamingContextBuilder b = new SimpleNamingContextBuilder();
      b.bind("java:comp/DefaultManagedScheduledExecutorService",
             Executors.newScheduledThreadPool(5));
      b.activate();

      //bootstrapping spring
      ApplicationContext context =
                new AnnotationConfigApplicationContext(MyConfig.class);
      MyBean bean = context.getBean(MyBean.class);
      bean.runTask();
      //shutdown after 10 sec
      Thread.sleep(10000);
      DefaultManagedTaskScheduler scheduler = context.getBean(
                DefaultManagedTaskScheduler.class);
      Executor exec = scheduler.getConcurrentExecutor();
      ExecutorService es = (ExecutorService) exec;
      es.shutdownNow();
  }
}

Output

Dec 21, 2016 2:25:31 AM org.springframework.mock.jndi.SimpleNamingContextBuilder bind
INFO: Static JNDI binding: [java:comp/DefaultManagedScheduledExecutorService] = [java.util.concurrent.ScheduledThreadPoolExecutor@eed1f14[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]]
Dec 21, 2016 2:25:31 AM org.springframework.mock.jndi.SimpleNamingContextBuilder activate
INFO: Activating simple JNDI environment
Dec 21, 2016 2:25:31 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@377dca04: startup date [Wed Dec 21 02:25:31 CST 2016]; root of context hierarchy
running 02:25:31.647
running 02:25:32.653
running 02:25:33.654
running 02:25:34.654
running 02:25:35.655
running 02:25:36.656
running 02:25:37.657
running 02:25:38.659
running 02:25:39.660
running 02:25:40.661
shutting down executor service




Example Project

Dependencies and Technologies Used:

  • Spring Context 4.3.4.RELEASE: Spring Context.
  • Spring TestContext Framework 4.3.4.RELEASE: Spring TestContext Framework.
  • JDK 1.8
  • Maven 3.3.9

Spring Task Executor Examples Select All Download
  • spring-core-task-scheduler
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also