Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring async method called from another async method

I'm using Spring 4 and I've noticed an odd behaviour... if I'm calling an async method multiple times from a normal instance method then they are all called in different threads and finish at random times. But if I call multiple times an async method from another async method then they finish in order. I have something like this:

@Async public void nonAsyncMethod() {   for (int i = 0; i < 30; i++) {      asyncMethod();   } }  @Async public void asyncMethod() {    ... something here } 

I'm using the default async executor. Should I use a different one? However this executor do not reuse any threads and starts another one every time so it should be fine... Can it be just a coincidence? But I've tried like more than 10 times and if I revert back to non-async for the first method then they finish randomly

like image 299
spauny Avatar asked Jul 22 '14 21:07

spauny


People also ask

Can we call asynchronous methods from another asynchronous method?

There's no rule that says that "asynchronous cannot call asynchronous". There are specific rules in place, such as "future cannot call future". A Queueable can call another Queueable, a Batchable can call another Batchable in the finish method, and Scheduleable methods can call Batchable and Queueable methods.

Can we call async method from async method Java?

Async with @Async Users can use it only on public methods and can't call the methods from the same class where they are defined. Any code that is inside a method annotated with Async will be executed in a different thread and can be void or can return a CompletableFuture.

What does @async annotation do?

The @EnableAsync annotation switches on Spring's ability to run @Async methods in a background thread pool. This class also customizes the Executor by defining a new bean. Here, the method is named taskExecutor , since this is the specific method name for which Spring searches.

What will happen if you specify @async over a public method of a Spring bean?

Simply put, annotating a method of a bean with @Async will make it execute in a separate thread. In other words, the caller will not wait for the completion of the called method.


1 Answers

What you are describing is a classic pitfall of Spring AOP.

In short, for Spring to be able to provide the async behavior it needs to create a proxy for your class at runtime. The proxy then does whatever it needs to do before and/or after calling your code. But in your case, the proxy mechanism is not being applied for the second method.

When a bean of your class is injected via Spring into some other component, Spring really injects the proxy instead. Therefor the relevant method of the proxy is called. However, when you are calling a method from inside the class, the limitations of Spring AOP mean the proxy never comes into play, but instead the regular method is called - with no extra features.

That is why asyncMethod is always executing on the same thread as the other method in the same class that called it.

Check out this excellent blog post as well as this part of Spring documentation.

There are some ways around the problem (check out this) that don't require you to refactor your code, but if you want async to work on both methods no matter what, the simplest thing to do is refactor the second method into another class.

like image 58
geoand Avatar answered Sep 28 '22 04:09

geoand