springboot and @Async examples

Introduction

This post would demo springboot and @Async examples, include:

  • springboot with @Async hello world example
  • springboot @Async with specific thread pool example
  • springboot @Async with return value example

Environments

  • SpringBoot 1.5.12
  • Java 1.8

The Pom.xml

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.12.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

What is the @Async annotation

The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously. In other words, the caller will return immediately upon invocation and the actual execution of the method will occur in a task that has been submitted to a Spring TaskExecutor.

In short words, @Async let you do the threading more easily in springboot applications. You don’t need to create a thread to execute the task anymore.

1. springboot with @Async hello world example

1.1 Enable the async with @EnableAsync on @SpringBootApplication

You must enable the async on SpringBootApplication like this:

@SpringBootApplication
@EnableAsync // The most import part
public class MainApp {
    public static void main(String[] args) {
        SpringApplication.run(MainApp.class, args);
    }
}

As the following code shows, the @EnableAsync is just a class annotation with some configuration for async tasks.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
...

1.2 Use the @Async in code

The @Async usage example code:

@Component
public class AsyncTester {
    private static Log log = LogFactory.getLog(AsyncTester.class);

    @Async
    public void asyncHelloWorld() {
        log.info("hi,springboot async,"
                +Thread.currentThread().getName());
    }
    ...

As the code shows, you just need to add the @Async annotation to a method,then everything is done.

1.3 Test the @Async code

and the test code is:

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAsync {
    @Autowired
    private AsyncTester asyncTester;

    @Test
    public void testAsyncHello() {
        asyncTester.asyncHello();
    }
    ....

1.4 Run the Testcase

Run the springboot application ,and we get this result:

2018-04-29 20:50:22.970  INFO 33263 --- [      cTaskExecutor-1] java8.learn.async.AsyncTester            : hi,springboot async hello,SimpleAsyncTaskExecutor-1

Here, we notice that the thread’s name is SimpleAsyncTaskExecutor, which is the default thread pool executor of the @Async task:

SimpleAsyncTaskExecutor This implementation does not reuse any threads, rather it starts up a new thread for each invocation. However, it does support a concurrency limit which will block any invocations that are over the limit until a slot has been freed up. If you are looking for true pooling, see the discussions of SimpleThreadPoolTaskExecutor and ThreadPoolTaskExecutor below.

By default when specifying @Async on a method, the executor that will be used is the one supplied to the ‘annotation-driven’ element as described above. However, the value attribute of the @Async annotation can be used when needing to indicate that an executor other than the default should be used when executing a given method.

2. springboot @Async with specific thread pool example

If we want the @Async task to run in a specified thread pool, we can define as follows:

@Configuration
public class ExecutorServiceConfig {
    @Bean
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(5);
        executor.setThreadNamePrefix("MyAsync-");
        executor.initialize();
        return executor;
    }
}

Rerun the same testcase code, we get this:

2018-04-29 20:50:22.970  INFO 33263 --- [      MyAsync-1] java8.learn.async.AsyncTester            : hi,springboot async hello,MyAsync-1

The thread name changed according to the Executor we defined.

we can also customize the name of the Executor like this:

@Configuration
public class ExecutorServiceConfig {
    //define an ExecutorService with fixed thread pool,and with a name of customFixedThreadPool
    @Bean("customFixedThreadPool")
    public ExecutorService customFixedThreadPool() {
        return Executors.newFixedThreadPool(2,new CustomizableThreadFactory("customFixedThreadPool"));
    }
}

//Use custom Executor named customFixedThreadPool
@Async("customFixedThreadPool")
public void async1() {
    log.info("hi,springboot async,"
            +Thread.currentThread().getName());
}

And run the same testcase code ,we get this:

2018-04-29 21:04:06.772  INFO 33282 --- [ixedThreadPool1] java8.learn.async.AsyncTester            : hi,springboot async,customFixedThreadPool1

As we can see, the name of the thread has changed to customFixedThreadPool1

3. springboot @Async with return value example

Sometimes, we want to execute a task with return value, we can use Future to achieve this:

@Async("customFixedThreadPool")
public Future<String> asyncWithResult() {
    try {
        Thread.sleep(2*1000);
        log.info("hi,springboot async with return value,"
                +Thread.currentThread().getName());
        return new AsyncResult<String>("hi springboot async");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return null;
}

Explanation:

  • This async methos has a return type **@Future**, means that this method must do a task and return a String
  • This method sleep for a while and return a **AsyncResult**, this is a holder of the future value.

The AsyncValue definition:

public class AsyncResult<V>
extends java.lang.Object
implements ListenableFuture<V>
...

And the testcase is:

@Test
public void testAsyncWithFuture() {
    Future<String> result = asyncTester.asyncWithResult();
    try {
        assertTrue(result.get().equals("hi springboot async"));
    } catch (Exception e) {
        e.printStackTrace();
        assertTrue(false);
    }
}

In this testcase, we call the async method and make sure the returned value is hi springboot async

run the code, we get the green bar and the output:

2018-04-29 21:10:32.869  INFO 33291 --- [fixedThreadPool1] java8.learn.async.AsyncTester            : hi,springboot async with return value,customFixedThreadPool1

It’s so easy, do you think so? You can find the complete code on github repo

You can find detail documents about the springboot and unit testing here: