# @Async annotation in Spring Boot

In 
Published 2023-06-10

This tutorial explains how we can use @Async annotation in Java Spring Boot.

@Async annotation in Spring Boot is used for telling Spring to run that method on another thread, so in parallel with the parent thread.

In order to use @Async annotation we need to:

1) Enable Async Support for the application This is done by adding @EnableAsync at the main application class or in the configuration class which is used to start the application.

2) Add the @Async annotation at the method level

Please take a look at the following example and read carefully the comments. The code is self-explanatory.

From the base application downloaded from Spring Initializr, I updated the main class and I added some new classes as below:

MyService1.java
package com.example.demo;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class MyService1 {
    @Async
    // This method runs Async but not value is returned
    public void myMethod() throws InterruptedException {
        System.out.println(">>>>> MyService1 - start ... Thread="+Thread.currentThread().getName());
        Thread.sleep(2000);
        System.out.println(">>>>> MyService1 - end ... Thread="+Thread.currentThread().getName());
    }
}
MyService2.java
package com.example.demo;

import org.springframework.stereotype.Service;

@Service
public class MyService2 {
    // This method runs in synchronous mode
    public void myMethod() throws InterruptedException {
        System.out.println(">>>>> MyService2 - start ... Thread="+Thread.currentThread().getName());
        Thread.sleep(4000);
        System.out.println(">>>>> MyService2 - end ... Thread="+Thread.currentThread().getName());
    }
}
MyService3.java
package com.example.demo;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class MyService3 {

    // This method runs Async but, and return a value
    @Async("completableFutureTaskExecutor")
    public CompletableFuture<String> myMethod() throws InterruptedException {
        System.out.println(">>>>> MyService3 - start ... Thread="+Thread.currentThread().getName());
        Thread.sleep(4000);
        System.out.println(">>>>> MyService3 - end ... Thread="+Thread.currentThread().getName());

        var returnVal = "Value returned by MyService3";
        return CompletableFuture.completedFuture(returnVal);
    }
}
DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

@SpringBootApplication
@EnableAsync
public class DemoApplication {

	@Bean
	@Primary
	// Define an executor for the ASYNC tasks
	// This is not mandatory, but it is useful in PROD to establish some limits
	public Executor defaultTaskExecutor1() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setQueueCapacity(100);
		executor.setCorePoolSize(2);
		executor.setMaxPoolSize(3);
		executor.setThreadNamePrefix("DemoApp-");
		executor.initialize();
		return executor;
	}

	@Bean
	// Define an executor for the ASYNC tasks
	// This is not mandatory, but it is useful in PROD to establish some limits
	public Executor completableFutureTaskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setQueueCapacity(100);
		executor.setCorePoolSize(2);
		executor.setMaxPoolSize(3);
		executor.setThreadNamePrefix("CF-");
		executor.initialize();
		return executor;
	}

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		// Create the application context and start Spring Boot
		ApplicationContext appContext = SpringApplication.run(DemoApplication.class, args);

		System.out.println("DemoApplication - start");

        // Get a Bean fron the application context and run a method
		MyService1 myService1 = appContext.getBean(MyService1.class);
		MyService2 myService2 = appContext.getBean(MyService2.class);
		MyService3 myService3 = appContext.getBean(MyService3.class);

		System.out.println("MAIN Thread="+Thread.currentThread().getName());

		myService1.myMethod();
		myService2.myMethod();
		CompletableFuture<String> cf = myService3.myMethod();
		cf.get();

		System.out.println("DemoApplication - end");
	}
}

When I run this code I get the following log:

DemoApplication - start
MAIN Thread=main
>>>>> MyService2 - start ... Thread=main
>>>>> MyService1 - start ... Thread=DemoApp-1
>>>>> MyService1 - end ... Thread=DemoApp-1
>>>>> MyService2 - end ... Thread=main
>>>>> MyService3 - start ... Thread=CF-1
>>>>> MyService3 - end ... Thread=CF-1
DemoApplication - end