Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring SimpleAsyncTaskExecutor and ThreadPoolTaskExecutor with @Async annotation

I have the following configutation:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <context:component-scan base-package="com.abc" />
    <task:annotation-driven executor="executor"/>
    <task:executor id="executor" pool-size="2"/>

</beans>

Then the following class

public class SomeClassImpl implements SomeClass {

   @Async
   @Override // overridden from the interface
   public void doSomething(){
      System.out.println("doing something async");
   }
}

A test:

@ContextConfiguration("classpath:test-config.xml") // with the xml config above
@RunWith(value = SpringJUnit4ClassRunner.class)
public class SomeClassTest {

   @Autowired
   private SomeClass someClass;

   @Test
   public void testSomething() throws Exception {

      System.out.println("Calling doSomething");
      someClass.doSomething();
      Thread.sleep(5000);
   }
}

When I ran the test, everything worked as expected. But then I attached the debugger to step through what is actually happening when someClass.doSomething() is called and i noticed the following:

enter image description here

Why is it that 4 threads are being created by a SimpleAsyncTaskExecutor? I know that if I remove the executor property from the task:annotation-driven xml element, the AsyncExecutionInterceptor will use the SimpleAsyncTaskExecutor. But since I have declared a task executor and referenced it from the annotation-driven element, why is a SimpleAsyncTaskExecutor being created?

like image 580
CAL5101 Avatar asked Nov 15 '12 16:11

CAL5101


People also ask

What is @async Annotation in Spring?

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 is @async Annotation in Java?

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. One interesting aspect in Spring is that the event support in the framework also has support for async processing if necessary.

What is the use of @EnableAsync annotation?

Annotation Type EnableAsync. Enables Spring's asynchronous method execution capability, similar to functionality found in Spring's <task:*> XML namespace.

Does @async work on private method?

Never use @Async on top of a private method. In runtime, it will not able to create a proxy and, therefore, not work.


1 Answers

First of all, if using Java 5 and above (required for Java Futures, etc), the default TaskExecutor implementation should be the ThreadPoolTaskExecutor, which matches your debug trace output. This is the thread manager actually executing your test code.

The SimpleAsyncTaskExecutor is likely being launched as part of the Spring task executor framework, possibly from another annotation in your test context file. The Spring container may be launching four instances of the SimpleAsyncTaskExecutor class. According to Spring documentation, this version of the TaskExecutor never re-uses threads to satisfy new requests (it will start a new thread):

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're looking for true pooling, keep scrolling further down the page.

Reference: http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/scheduling.html#scheduling-task-executor

Therefore, this could be the result of interactions between the test context and the Spring container.

I believe you have a single thread running your test code in this scenario, which is what you expect, based on a single request. Spring TaskExecutor implementations leverage a helper class called ConcurrencyThrottleSupport which is supposed to throttle (limit) the number of concurrent threads in execution. In your case, this should be 2, as indicated by the pool-size property. However, to run this one test, it should never need to allocate an additional thread, and the trace output agrees with this.

like image 159
pmhargis Avatar answered Oct 21 '22 15:10

pmhargis