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:
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?
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.
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.
Annotation Type EnableAsync. Enables Spring's asynchronous method execution capability, similar to functionality found in Spring's <task:*> XML namespace.
Never use @Async on top of a private method. In runtime, it will not able to create a proxy and, therefore, not work.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With