The Spring TaskExecutor
Abstraction
Executors are the JDK name for the concept of thread pools. The “executor” naming is due to the fact that there is no guarantee that the underlying implementation is actually a pool. An executor may be single-threaded or even synchronous. Spring’s abstraction hides implementation details between the Java SE and Java EE environments.
Spring’s TaskExecutor
interface is identical to the java.util.concurrent.Executor
interface. In fact, originally, its primary reason for existence was to abstract away
the need for Java 5 when using thread pools. The interface has a single method
(execute(Runnable task)
) that accepts a task for execution based on the semantics
and configuration of the thread pool.
The TaskExecutor
was originally created to give other Spring components an abstraction
for thread pooling where needed. Components such as the ApplicationEventMulticaster
,
JMS’s AbstractMessageListenerContainer
, and Quartz integration all use the
TaskExecutor
abstraction to pool threads. However, if your beans need thread pooling
behavior, you can also use this abstraction for your own needs.
TaskExecutor
Types
Spring includes a number of pre-built implementations of TaskExecutor
.
In all likelihood, you should never need to implement your own.
The variants that Spring provides are as follows:
-
SyncTaskExecutor
: This implementation does not execute invocations asynchronously. Instead, each invocation takes place in the calling thread. It is primarily used in situations where multi-threading is not necessary, such as in simple test cases. -
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 that blocks any invocations that are over the limit until a slot has been freed up. If you are looking for true pooling, seeThreadPoolTaskExecutor
, later in this list. -
ConcurrentTaskExecutor
: This implementation is an adapter for ajava.util.concurrent.Executor
instance. There is an alternative (ThreadPoolTaskExecutor
) that exposes theExecutor
configuration parameters as bean properties. There is rarely a need to useConcurrentTaskExecutor
directly. However, if theThreadPoolTaskExecutor
is not flexible enough for your needs,ConcurrentTaskExecutor
is an alternative. -
ThreadPoolTaskExecutor
: This implementation is most commonly used. It exposes bean properties for configuring ajava.util.concurrent.ThreadPoolExecutor
and wraps it in aTaskExecutor
. If you need to adapt to a different kind ofjava.util.concurrent.Executor
, we recommend that you use aConcurrentTaskExecutor
instead. -
WorkManagerTaskExecutor
: This implementation uses a CommonJWorkManager
as its backing service provider and is the central convenience class for setting up CommonJ-based thread pool integration on WebLogic or WebSphere within a Spring application context. -
DefaultManagedTaskExecutor
: This implementation uses a JNDI-obtainedManagedExecutorService
in a JSR-236 compatible runtime environment (such as a Java EE 7+ application server), replacing a CommonJ WorkManager for that purpose.
Using a TaskExecutor
Spring’s TaskExecutor
implementations are used as simple JavaBeans. In the following example,
we define a bean that uses the ThreadPoolTaskExecutor
to asynchronously print
out a set of messages:
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
}
As you can see, rather than retrieving a thread from the pool and executing it yourself,
you add your Runnable
to the queue. Then the TaskExecutor
uses its internal rules to
decide when the task gets executed.
To configure the rules that the TaskExecutor
uses, we expose simple bean properties:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5"/>
<property name="maxPoolSize" value="10"/>
<property name="queueCapacity" value="25"/>
</bean>
<bean id="taskExecutorExample" class="TaskExecutorExample">
<constructor-arg ref="taskExecutor"/>
</bean>