Spring 3.x - @Async methods not called concurrently by task executor

10,466

The issue is that you're calling the methods internally so they're not proxied. For the @Async to work you need to retrieve the object from your application context and invoke the methods on the retrieved copy. Invoking it internally will not work.

The same thing happens if you try to invoke internal @Transactional methods. See the Note: section at the end of the Spring docs explaining @Transactional for details.

Additionally, the way you're immediately calling .get() on the Future return values is incorrect. If you want them to happen in parallel you should submit all the tasks then retrieve each one via .get().

The usual approach is to handle the proxying issue is to create a separate service class that gets TestService injected into it and calls the @Async service methods directly:

@Service public class TestServiceHelper {
    @Autowired
    TestService testService;

    public SomeDataType getSOmeDataType() {
        try {
            // Invoke all of them async:
            Future<List<DataType>> a = testService.retrieveDataA();
            Future<List<DataType>> b = testService.retrieveDataA();
            Future<List<DataType>> c = testService.retrieveDataA();
            Future<List<DataType>> d = testService.retrieveDataA();
            Future<List<DataType>> e = testService.retrieveDataA();

            // Wait for each sequentially:
            List<DataType> aList = a.get();
            List<DataType> bList = b.get();
            List<DataType> cList = c.get();
            List<DataType> dList = d.get();
            List<DataType> eList = e.get();

            // do work with lists here ...
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        catch (ExecutionException e) {
            e.printStackTrace();
        }
        return referralDetailsReferenceData;
    }
}

An alternative to the above separate service bean (quite hackish/not recommended!) is to inject the object into itself and use the injected copy to invoke the @Async methods.

Share:
10,466
tarares
Author by

tarares

Updated on June 05, 2022

Comments

  • tarares
    tarares almost 2 years

    I'm trying to implement concurrent method invocation in my Service class.

    I've some methods annotated as @Async in my service class and I'm trying to call all these methods concurrently. But the methods are executing sequentially.

    This is my service class(dummy):

    @Service public class TestService {
    
    public SomeDataType getSOmeDataType() {
            try {
                List<DataType> a = retrieveDataA().get();
                List<DataType> b = retrieveDataB().get();
                List<DataType> c = retrieveDataC().get();
                List<DataType> d = retrieveDataD().get();
                List<DataType> e = retrieveDataE().get();           
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            catch (ExecutionException e) {
                e.printStackTrace();
            }
            return referralDetailsReferenceData;
        }
    
    @Async 
        private Future<List<DataType>> retrieveDataA() {
            //method logic
        }
    
    @Async 
        private Future<List<DataType>> retrieveDataB() {
            //method logic
        }
    
    @Async 
        private Future<List<DataType>> retrieveDataC() {
            //method logic
        }
    
    @Async 
        private Future<List<DataType>> retrieveDataD() {
            //method logic
        }
    
    @Async 
        private Future<List<DataType>> retrieveDataE() {
            //method logic
        }
    

    This is my spring config:

    <bean id="executorService" class="java.util.concurrent.Executors" factory-method="newFixedThreadPool">
        <constructor-arg value="10" />
    </bean>
    
    <task:executor id="threadPoolTaskExecutor" pool-size="10" />
    
    <task:annotation-driven executor="executorService" />
    

    When "getSomeDataType" is executed the methods are invoked sequentially.

    I'm new to @Async and concurrent execution in Spring, so I'm sure I'm doing something silly. But I'm not able to figure it out.

    Any suggestions is highly appreciated.