Spring - Asynchronous Execution using @Async

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

Spring provides annotation support for asynchronous method execution via @Async and @EnableAsync.

We are going to explore different aspects of this feature with quick examples.



Simple use of @Async and @EnableAsync

Annotate the configuration class with @EnableAsync

@EnableAsync
@Configuration
public class MyConfig {
   @Bean
   public MyBean myBean () {
       return new MyBean();
   }
}

Note that we don't have to provide a TaskExecutor as a bean. Spring will use a default executor implicitly.


Use @Async on bean's methods:

public class MyBean {

   @Async
   public void runTask () {
       System.out.printf("Running task  thread: %s%n",
                         Thread.currentThread().getName());
   }
}

Calling async method from main method:

public class AsyncExample {

    public static void main (String[] args) {
        AnnotationConfigApplicationContext context =
                  new AnnotationConfigApplicationContext(MyConfig.class);


        MyBean bean = context.getBean(MyBean.class);
        System.out.printf("calling async method from thread: %s%n",
                          Thread.currentThread().getName());

        bean.runTask();
    }
}

Output

calling async method from thread: main
Running task  thread: SimpleAsyncTaskExecutor-1


Using Executor other than default:

To specify a custom Executor we just need to configure it as a bean:

@EnableAsync
@Configuration
public class MyConfig {
    @Bean
    public MyBean myBean () {
        return new MyBean();
    }

    @Bean
    public TaskExecutor taskExecutor () {
        return new ConcurrentTaskExecutor(
                  Executors.newFixedThreadPool(3));
    }

}

MyBean and main classes are same. This time we will have this output:

calling async method from thread: main
Running task  thread: pool-1-thread-1


Using a qualifier

If we have multiple executors configured as beans; we can resolve conflict between them by using a qualifier with @Async("qualifierValue"):

@EnableAsync
@Configuration
public class MyConfig {
    @Bean
    public MyBean myBean () {
        return new MyBean();
    }

    @Bean
    @Qualifier("myExecutor1")
    public TaskExecutor taskExecutor2 () {
        return new ConcurrentTaskExecutor(
                  Executors.newFixedThreadPool(3));
    }

    @Bean
    @Qualifier("myExecutor2")
    public TaskExecutor taskExecutor () {
        return new ThreadPoolTaskExecutor();
    }
}
public class MyBean {

    @Async("myExecutor2")
    public void runTask () {
        System.out.printf("Running task  thread: %s%n",
                          Thread.currentThread().getName());
    }
}
public class AsyncExecutorWithQualifierExample {

    public static void main (String[] args) {
        AnnotationConfigApplicationContext context =
                  new AnnotationConfigApplicationContext(MyConfig.class);
        MyBean bean = context.getBean(MyBean.class);
        System.out.printf("calling async method from thread: %s%n",
                          Thread.currentThread().getName());
        bean.runTask();

        ThreadPoolTaskExecutor exec = context.getBean(ThreadPoolTaskExecutor.class);
        exec.getThreadPoolExecutor().shutdown();
    }
}

Output

calling async method from thread: main
Running task  thread: taskExecutor-1



Async method arguments and return value

The methods annotated with @Async can accept any method arguments. Using a return value will probably always return a null value if its type is other than Future.

@EnableAsync
@Configuration
public class MyConfig {
   @Bean
   public MyBean myBean () {
       return new MyBean();
   }
}
public class MyBean {

    @Async
    public String runTask (String message) {
        System.out.printf("Running task  thread: %s%n",
                          Thread.currentThread().getName());
        System.out.printf("message: %s%n", message);
        System.out.println("task ends");
        return "return value";
    }
}
public class AsyncArgAndReturnValueExample {

    public static void main (String[] args) {
        AnnotationConfigApplicationContext context =
                  new AnnotationConfigApplicationContext(
                            MyConfig.class);
        MyBean bean = context.getBean(MyBean.class);
        System.out.printf("calling MyBean#runTask() thread: %s%n",
                          Thread.currentThread().getName());
        String s = bean.runTask("from main");
        System.out.println("call MyBean#runTask() returned");
        System.out.println("returned value: " + s);
    }
}

Output

calling MyBean#runTask() thread: main
call MyBean#runTask() returned
returned value: null
INFO: No TaskExecutor bean found for async processing
Running task  thread: SimpleAsyncTaskExecutor-1
message: from main
task ends



Returning a Future instance from Async method

If Async methods return values, they should return an instance of java.util.concurrent.Future:

@EnableAsync
@Configuration
public class MyConfig {
    @Bean
    public MyBean myBean () {
        return new MyBean();
    }
}
public class MyBean {

   @Async
   public CompletableFuture<String> runTask () {
       System.out.printf("Running task  thread: %s%n",
                         Thread.currentThread().getName());

       CompletableFuture<String> future = new CompletableFuture<String>() {
           @Override
           public String get () throws InterruptedException, ExecutionException {
               return " task result";
           }
       };
       return future;
   }
}
public class AsyncReturningFutureExample {

public static void main (String[] args) throws Exception {
    AnnotationConfigApplicationContext context =
              new AnnotationConfigApplicationContext(MyConfig.class);
    MyBean bean = context.getBean(MyBean.class);
    System.out.printf("calling MyBean#runTask() thread: %s%n",
                      Thread.currentThread().getName());
    CompletableFuture<String> r = bean.runTask();
    System.out.println("result from task:" + r.get());
}

Output

calling MyBean#runTask() thread: main
Running task  thread: SimpleAsyncTaskExecutor-1
result from task: task result

Async method can also return Spring's ListenableFuture.




Using AsyncConfigurer

The interface AsyncConfigurer can be implemented by @Configuration classes to customize the Executor instance or handling exception thrown in async method by using AsyncUncaughtExceptionHandler:

@EnableAsync
@Configuration
public MyConfig implements AsyncConfigurer{
    @Bean
    public MyBean myBean () {
        return new MyBean();
    }

    @Bean("taskExecutor")
    @Override
    public Executor getAsyncExecutor () {
        return new ConcurrentTaskExecutor(
                  Executors.newFixedThreadPool(3));
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler () {
        return (throwable, method, objects) ->
                  System.out.println("-- exception handler -- "+throwable);
    }
}
public class MyBean {

   @Async
   public void runTask () {
       System.out.printf("Running task  thread: %s%n",
                         Thread.currentThread().getName());

       throw new RuntimeException("test exception");
   }
}
public class AsyncConfigurerExample {

    public static void main (String[] args) throws ExecutionException, InterruptedException {
        AnnotationConfigApplicationContext context =
                  new AnnotationConfigApplicationContext(
                            MyConfig.class);
        MyBean bean = context.getBean(MyBean.class);
        System.out.printf("calling MyBean#runTask() thread: %s%n",
                          Thread.currentThread().getName());
        bean.runTask();

        ConcurrentTaskExecutor exec =
                  (ConcurrentTaskExecutor) context.getBean("taskExecutor");
        ExecutorService es = (ExecutorService) exec.getConcurrentExecutor();
        es.shutdown();
    }
}

Output

calling MyBean#runTask() thread: main
Running task  thread: pool-1-thread-1
-- exception handler -- java.lang.RuntimeException: test exception


@Async on class level

@Async can also be used at the class level, in which case all of the class methods will be executed asynchronously.





Example Project

Dependencies and Technologies Used :

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

Spring Async Annotation Examples Select All Download
  • spring-core-async-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also