Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the executor specified by @Async work for CompletableFuture.supplyAsync(supplier)?

I have an executor,

@Bean("someExecutor")
public Executor someExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(2);
    executor.setMaxPoolSize(2);
    executor.setQueueCapacity(500);
    executor.setThreadNamePrefix("AsyncMethod-");
    executor.initialize();
    return executor;
}

and a async method.

@Async("someExecutor")
public Future<List<String>> someMethod(){
    return CompletableFuture.supplyAsync(() -> {
        //long time job
        return listGeneratedByLongTimeJob;
    });
}

Will Spring use the someExecutor for someMethod? And how?

I know an overloading method for supplyAsync(supplier) is supplyAsync(supplier, executor), how about the following code?

@Autowired("someExecutor")
private Executor executor;

@Async()
public Future<List<String>> someMethod(){
    return CompletableFuture.supplyAsync(() -> {
        //long time job
        return listGeneratedByLongTimeJob;
    }, executor);
}

Thanks.

like image 572
Bing Zhao Avatar asked Mar 04 '26 03:03

Bing Zhao


1 Answers

Okay this taken while to figure this out, and before going to two scenarios we need to discuss about ForkJoinPool

from java.util.concurrent.CompletableFuture docs

All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interface CompletableFuture.AsynchronousCompletionTask.

So when every you are calling CompletableFuture.supplyAsync(Supplier s), supplier will be executed by ForkJoinPool thread, now lets start case 1

Case 1:

To make it clear i have added some sysout statements to print thread names

@Async("someExecutor")
public Future<String> asyncService() {
    System.out.println();

    System.out.println(Thread.currentThread().getName()+" - "+Thread.currentThread().getThreadGroup());

    System.out.println();
    return CompletableFuture.supplyAsync(()->{

    System.out.println(Thread.currentThread().getName()+" - "+Thread.currentThread().getThreadGroup());
        return "hello";
    });

}

Output:

AsyncMethod-1 - java.lang.ThreadGroup[name=main,maxpri=10]

ForkJoinPool.commonPool-worker-1 - java.lang.ThreadGroup[name=main,maxpri=10]

In this case asyncService() is executed by AsyncMethod-1 thread and, supplier in supplyAsync() is executed by ForkJoinPool

case 2:

@Autowired
private Executor someExecutor;


@Async
public Future<String> asyncService() {
    System.out.println();

    System.out.println(Thread.currentThread().getName()+" - "+Thread.currentThread().getThreadGroup());

    System.out.println();
    return CompletableFuture.supplyAsync(()->{

    System.out.println(Thread.currentThread().getName()+" - "+Thread.currentThread().getThreadGroup());
        return "hello";
    },someExecutor);

}

output:

AsyncMethod-1 - java.lang.ThreadGroup[name=main,maxpri=10]

AsyncMethod-2 - java.lang.ThreadGroup[name=main,maxpri=10]

In the second case asyncService() method and supplier in supplyAsync() both are using threads fromsomeExecutor pool

By default, Spring uses a SimpleAsyncTaskExecutor to actually run these async methods, but we overridden that with someExecutor in config using @EnableAsync docs

By default, Spring will be searching for an associated thread pool definition: either a unique TaskExecutor bean in the context, or an Executor bean named "taskExecutor" otherwise. If neither of the two is resolvable, a SimpleAsyncTaskExecutor will be used to process async method invocations.

NOTE : If you don't have @EnableAsync in config class you will get different results, i will upload this code in gitHub and add link here

like image 98
Deadpool Avatar answered Mar 06 '26 18:03

Deadpool