Spring - Task Execution

[Updated: Dec 21, 2016, Created: Dec 16, 2016]

Spring Core framework provides abstraction of task execution. Task execution is referred as creating a new thread and perform some task in it. Spring's task execution abstraction hides implementation differences between Java SE 1.4, Java SE 5 and Java EE environments. Spring underlying implementation can be based on Java Executor framework or a simple Java thread or even it can be based on a third party library like Quartz.



The interfaces


java.util.concurrent.ExecutorExecutororg.springframework.core.task.TaskExecutorTaskExecutororg.springframework.core.task.AsyncTaskExecutorAsyncTaskExecutororg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutorLogicBig

Spring task execution is based on the TaskExecutor and it's sub-interfaces.

The diagram on the left is interactive and clickable, it will take you the api pages.

  • TaskExecutor is same as Executor interface.
  • AsyncTaskExecutor can handle Callable submission and returns Future.
  • AsyncListenableTaskExecutor returns ListenableFuture on task submission, which can attach listener to get callbacks on task completion.

We going to quickly go through implementations with examples.



SimpleAsyncTaskExecutor


java.lang.ObjectObjectorg.springframework.util.CustomizableThreadCreatorCustomizableThreadCreatorjava.io.SerializableSerializableorg.springframework.core.task.SimpleAsyncTaskExecutorSimpleAsyncTaskExecutororg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutorjava.io.SerializableSerializableLogicBig

This implementation of AsyncListenableExecutor creates a new Thread for each task execution. Here's a standalone example:

import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;

public class TaskExecutorStandAloneExample {

    public static void main (String... strings) {
        TaskExecutor theExecutor = new SimpleAsyncTaskExecutor();

        theExecutor.execute(new Runnable() {
            @Override
            public void run () {
                System.out.println("running task in thread: " +
                                                         Thread.currentThread()
                                                               .getName());
            }
        });

        System.out.println("in main thread: " + Thread.currentThread()
                                                      .getName());

    }
}

Output

in main thread: main
running task in thread: SimpleAsyncTaskExecutor-1


Using executor as a bean

 @Configuration
 public  class MyConfig {

     @Bean
     MyBean myBean () {
         return new MyBean();
     }

     @Bean
     AsyncTaskExecutor taskExecutor () {
         SimpleAsyncTaskExecutor t = new SimpleAsyncTaskExecutor();
         t.setConcurrencyLimit(100);
         return t;
     }
 }
public class MyBean {
  @Autowired
  private AsyncTaskExecutor executor;

  public void runTasks () throws Exception {
      List<Future<?>> futureList = new ArrayList<>();

      for (int i = 0; i < 10; i++) {
          Future<?> future = executor.submit(getTask(i));
          futureList.add(future);
      }

      for (Future<?> future : futureList) {
          System.out.println(future.get());
      }
  }

  private Callable<String> getTask (int i) {
       return () -> {
          System.out.printf("running task %d. Thread: %s%n",
                            i,
                            Thread.currentThread().getName());
           return String.format("Task finished %d", i);
      };
  }
}
public class AsyncTaskExecutorExample {
  public static void main (String[] args) throws Exception {
   ApplicationContext context =
             new AnnotationConfigApplicationContext(MyConfig.class);
   MyBean bean = context.getBean(MyBean.class);
   bean.runTasks();
  }
}

Output

running task 0. Thread: SimpleAsyncTaskExecutor-1
running task 3. Thread: SimpleAsyncTaskExecutor-4
running task 2. Thread: SimpleAsyncTaskExecutor-3
running task 9. Thread: SimpleAsyncTaskExecutor-10
running task 1. Thread: SimpleAsyncTaskExecutor-2
running task 8. Thread: SimpleAsyncTaskExecutor-9
running task 7. Thread: SimpleAsyncTaskExecutor-8
running task 6. Thread: SimpleAsyncTaskExecutor-7
Task finished 0
Task finished 1
Task finished 2
Task finished 3
running task 5. Thread: SimpleAsyncTaskExecutor-6
running task 4. Thread: SimpleAsyncTaskExecutor-5
Task finished 4
Task finished 5
Task finished 6
Task finished 7
Task finished 8
Task finished 9



ListenableFutureCallback example

As SimpleAsyncTaskExecutor also implements AsyncListenableTaskExecutor, we can add callback listeners, instead of blocking on Future.get() call.

@Configuration
public class MyConfig {

    @Bean
    MyBean myBean () {
        return new MyBean();
    }

    @Bean
    AsyncListenableTaskExecutor taskExecutor () {
        SimpleAsyncTaskExecutor t = new SimpleAsyncTaskExecutor();
        t.setConcurrencyLimit(100);
        return t;
    }

    @Bean
    ListenableFutureCallback<String> taskCallback () {
        return new MyListenableFutureCallback();
    }
}
public class MyBean {
    @Autowired
    private AsyncListenableTaskExecutor executor;
    @Autowired
    private ListenableFutureCallback threadListenableCallback;

    public void runTasks () throws Exception {
        for (int i = 0; i < 10; i++) {
            ListenableFuture<String> f = executor.submitListenable(getTask(i));
            f.addCallback(threadListenableCallback);
        }
    }

    private Callable<String> getTask (int i) {
        return () -> {
            System.out.printf("running task %d. Thread: %s%n",
                              i,
                              Thread.currentThread().getName());
            return String.format("Task finished %d", i);
        };
    }
}
 public class MyListenableFutureCallback implements ListenableFutureCallback<String> {
    @Override
    public void onFailure (Throwable ex) {
        ex.printStackTrace();
    }

    @Override
    public void onSuccess (String result) {
        System.out.println("success object: " + result);
    }
}
public class AsyncListableTaskExecutorExample{
  public static void main (String[] args) throws Exception {
    ApplicationContext context =
              new AnnotationConfigApplicationContext(MyConfig.class);
    MyBean bean = context.getBean(MyBean.class);
    bean.runTasks();
  }
}
running task 0. Thread: SimpleAsyncTaskExecutor-1
running task 1. Thread: SimpleAsyncTaskExecutor-2
running task 2. Thread: SimpleAsyncTaskExecutor-3
success object: Task finished 0
success object: Task finished 2
running task 5. Thread: SimpleAsyncTaskExecutor-6
success object: Task finished 1
running task 4. Thread: SimpleAsyncTaskExecutor-5
running task 3. Thread: SimpleAsyncTaskExecutor-4
running task 8. Thread: SimpleAsyncTaskExecutor-9
running task 9. Thread: SimpleAsyncTaskExecutor-10
success object: Task finished 4
running task 7. Thread: SimpleAsyncTaskExecutor-8
running task 6. Thread: SimpleAsyncTaskExecutor-7
success object: Task finished 5
success object: Task finished 6
success object: Task finished 7
success object: Task finished 9
success object: Task finished 8
success object: Task finished 3



ThreadPoolTaskExecutor

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.ThreadPoolTaskExecutorThreadPoolTaskExecutororg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutororg.springframework.scheduling.SchedulingTaskExecutorSchedulingTaskExecutorLogicBig
java.util.concurrent.ExecutorExecutororg.springframework.core.task.TaskExecutorTaskExecutororg.springframework.core.task.AsyncTaskExecutorAsyncTaskExecutororg.springframework.scheduling.SchedulingTaskExecutorSchedulingTaskExecutorLogicBig

This implementation is the most commonly used executor. It is based on JSE ScheduledThreadPoolExecutor.

It exposes bean style configuration of "corePoolSize", "maxPoolSize", "keepAliveSeconds", "queueCapacity" etc.

As seen in the left diagram, it also implements Spring's SchedulingTaskExecutor interface. This interface is not delay/period based but rather exposes scheduling characteristics of a particular implementation. We will explore periodic scheduling executor (which is based on TaskScheduler interface) in the next tutorial.

Following example demonstrates how to use it as a bean:

 @Configuration
  public class MyConfig {

      @Bean
      MyBean myBean () {
          return new MyBean();
      }

      @Bean
      TaskExecutor taskExecutor () {
          ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor();
          t.setCorePoolSize(10);
          t.setMaxPoolSize(100);
          t.setQueueCapacity(50);
          t.setAllowCoreThreadTimeOut(true);
          t.setKeepAliveSeconds(120);
          return t;
      }
}
public class MyBean {
   @Autowired
   private TaskExecutor executor;

   public void runTasks () {
       for (int i = 0; i < 10; i++) {
           executor.execute(getTask(i));

       }
   }

   private Runnable getTask (int i) {
        return () -> {
           System.out.printf("running task %d. Thread: %s%n",
                             i,
                             Thread.currentThread().getName());
       };
   }
}
public class ThreadPoolTaskExecutorExample{
  public static void main (String[] args) {
      ApplicationContext context =
                new AnnotationConfigApplicationContext(MyConfig.class);
      MyBean bean = context.getBean(MyBean.class);
      bean.runTasks();
      ThreadPoolTaskExecutor t =
                          context.getBean(ThreadPoolTaskExecutor.class);
        t.shutdown();
  }
}

Output

 running task 1. Thread: taskExecutor-2
running task 4. Thread: taskExecutor-5
running task 3. Thread: taskExecutor-4
running task 2. Thread: taskExecutor-3
running task 0. Thread: taskExecutor-1
running task 9. Thread: taskExecutor-10
running task 6. Thread: taskExecutor-7
running task 5. Thread: taskExecutor-6
running task 8. Thread: taskExecutor-9
running task 7. Thread: taskExecutor-8



ConcurrentTaskExecutor

java.lang.ObjectObjectorg.springframework.scheduling.concurrent.ConcurrentTaskExecutorConcurrentTaskExecutororg.springframework.core.task.AsyncListenableTaskExecutorAsyncListenableTaskExecutororg.springframework.scheduling.SchedulingTaskExecutorSchedulingTaskExecutorLogicBig

This implementation is an adapter for a java.util.concurrent.Executor object. The method ConcurrentTaskExecutor#setTaskDecorator() can be used to decorate the underlying executor. This decorator can be used to provide some monitoring/statistics for task execution.

Following is an example of ConcurrentTaskExecutor:

 @Configuration
  public class MyConfig {

      @Bean
      MyBean myBean () {
          return new MyBean();
      }

      @Bean
      TaskExecutor taskExecutor () {
          ConcurrentTaskExecutor t = new ConcurrentTaskExecutor(
                    Executors.newFixedThreadPool(3));
          t.setTaskDecorator(new TaskDecorator() {
              @Override
              public Runnable decorate (Runnable runnable) {
                  return () -> {

                      MyTask task = (MyTask) runnable;
                      long t = System.currentTimeMillis();
                      task.run();
                      System.out.printf("time taken for task: %s , %s%n",
                                        task.getI(),
                                        (System.currentTimeMillis() - t));
                  };
              }
          });
          return t;
      }
}
public class MyBean {
    @Autowired
    private TaskExecutor executor;

    public void runTasks () {
        for (int i = 0; i < 10; i++) {
            executor.execute(new MyTask(i));

        }
    }
}
public class MyTask implements Runnable {

   private final int i;

   MyTask (int i) {
       this.i = i;
   }

   @Override
   public void run () {
       try {
           Thread.sleep(400);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.printf("running task %d. Thread: %s%n",
                         i,
                         Thread.currentThread().getName());
   }


   public int getI () {
       return i;
   }
}
public class ConcurrentTaskExecutorExample {
   public static void main (String[] args) {
       ApplicationContext context =
                 new AnnotationConfigApplicationContext(MyConfig.class);
       MyBean bean = context.getBean(MyBean.class);
       bean.runTasks();
       ConcurrentTaskExecutor exec = context.getBean(ConcurrentTaskExecutor.class);
       ExecutorService service = (ExecutorService) exec.getConcurrentExecutor();
       service.shutdown();
   }
}

Output

running task 0. Thread: pool-1-thread-1
running task 1. Thread: pool-1-thread-2
time taken for task: 1 , 401
running task 2. Thread: pool-1-thread-3
time taken for task: 2 , 401
time taken for task: 0 , 401
running task 3. Thread: pool-1-thread-2
running task 4. Thread: pool-1-thread-3
time taken for task: 4 , 400
running task 5. Thread: pool-1-thread-1
time taken for task: 5 , 401
time taken for task: 3 , 400
running task 6. Thread: pool-1-thread-3
time taken for task: 6 , 400
running task 8. Thread: pool-1-thread-2
time taken for task: 8 , 400
running task 7. Thread: pool-1-thread-1
time taken for task: 7 , 400
running task 9. Thread: pool-1-thread-3
time taken for task: 9 , 400


In this tutorial we have gone through commonly used task executors. There are more. Please checkout reference document TaskExecutor types list. In the next tutorial we will explore Spring TaskScheduler implementation.





Example Project

Dependencies and Technologies Used :

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

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

See Also