Spring 2.0 introduces a new abstraction for dealing with executors. Executors are the Java 5 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 Java SE 1.4, Java SE 5 and Java EE environments.
Spring's TaskExecutor
interface is
identical to the java.util.concurrent.Executor
interface. In fact, its primary reason for existence is 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,
it is possible to use this abstraction for your own needs.
There are a number of pre-built implementations of
TaskExecutor
included with the
Spring distribution. In all likelihood, you shouldn't ever
need to implement your own.
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.
This implementation doesn't execute invocations asynchronously. Instead, each invocation takes place in the calling thread. It is primarily used in situations where mutlithreading isn't necessary such as simple test cases.
This implementation is a wrapper for a Java 5
java.util.concurrent.Executor
.
There is an alternative,
ThreadPoolTaskExecutor
,
that exposes the Executor
configuration parameters as bean properties. It
is rare to need to use the ConcurrentTaskExecutor
but if the
ThreadPoolTaskExecutor
isn't robust enough for your needs, the
ConcurrentTaskExecutor
is an alternative.
This implementation is actually a subclass of
Quartz's SimpleThreadPool
which listens to Spring's lifecycle callbacks.
This is typically used when you have a
threadpool that may need to be shared by both
Quartz and non-Quartz components.
This implementation can only be used in a Java 5
environment but is also the most commonly used
one in that environment. It exposes bean properties for
configuring a
java.util.concurrent.ThreadPoolExecutor
and wraps it in a TaskExecutor
.
If you need something advanced such as a
ScheduledThreadPoolExecutor
,
it is recommended that you use a
ConcurrentTaskExecutor
instead.
TimerTaskExecutor
This implementation uses a single
TimerTask
as its backing implementation. It's different
from the
SyncTaskExecutor
in that the method invocations are executed in a
separate thread, although they are synchronous
in that thread.
WorkManagerTaskExecutor
This implementation uses the CommonJ WorkManager
as its backing implementation and is the central
convenience class for setting up a CommonJ
WorkManager reference in a Spring context.
Similar to the
SimpleThreadPoolTaskExecutor
,
this class implements the WorkManager
interface and therefore can be used directly as
a WorkManager as well.
Spring's TaskExecutor
implementations
are used as simple JavaBeans. In the example below, 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 yourself, you add your Runnable
to the queue and the TaskExecutor
uses its internal rules to decide when the task gets executed.
To configure the rules that the TaskExecutor
will use, simple bean properties have been exposed.
<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>