Browse Source

Up-to-date coverage of task executor and scheduler variants

Includes a clarification of ThreadPoolExecutor configuration options and a note on early AsyncConfigurer initialization.

Issue: SPR-16944
Issue: SPR-16945

(cherry picked from commit d58c09b)
pull/1884/head
Juergen Hoeller 8 years ago
parent
commit
5a111125c1
  1. 5
      spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
  2. 12
      spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java
  3. 22
      spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java
  4. 112
      src/docs/asciidoc/integration.adoc

5
spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java

@ -76,6 +76,11 @@ import org.springframework.core.Ordered;
* method.</li> * method.</li>
* </ul> * </ul>
* *
* <p><b>NOTE: {@link AsyncConfigurer} configuration classes get initialized early
* in the application context bootstrap. If you need any dependencies on other beans
* there, make sure to declare them 'lazy' as far as possible in order to let them
* go through other post-processors as well.</b>
*
* <pre class="code"> * <pre class="code">
* &#064;Configuration * &#064;Configuration
* &#064;EnableAsync * &#064;EnableAsync

12
spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,6 +37,16 @@ import org.springframework.lang.Nullable;
* "queueCapacity" properties) and exposing it as a bean reference of its native * "queueCapacity" properties) and exposing it as a bean reference of its native
* {@link java.util.concurrent.ExecutorService} type. * {@link java.util.concurrent.ExecutorService} type.
* *
* <p>The default configuration is a core pool size of 1, with unlimited max pool size
* and unlimited queue capacity. This is roughly equivalent to
* {@link java.util.concurrent.Executors#newSingleThreadExecutor()}, sharing a single
* thread for all tasks. Setting {@link #setQueueCapacity "queueCapacity"} to 0 mimics
* {@link java.util.concurrent.Executors#newCachedThreadPool()}, with immediate scaling
* of threads in the pool to a potentially very high number. Consider also setting a
* {@link #setMaxPoolSize "maxPoolSize"} at that point, as well as possibly a higher
* {@link #setCorePoolSize "corePoolSize"} (see also the
* {@link #setAllowCoreThreadTimeOut "allowCoreThreadTimeOut"} mode of scaling).
*
* <p>For an alternative, you may set up a {@link ThreadPoolExecutor} instance directly * <p>For an alternative, you may set up a {@link ThreadPoolExecutor} instance directly
* using constructor injection, or use a factory method definition that points to the * using constructor injection, or use a factory method definition that points to the
* {@link java.util.concurrent.Executors} class. * {@link java.util.concurrent.Executors} class.

22
spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java

@ -48,11 +48,15 @@ import org.springframework.util.concurrent.ListenableFutureTask;
* providing several useful attributes: "corePoolSize", "maxPoolSize", "keepAliveSeconds" * providing several useful attributes: "corePoolSize", "maxPoolSize", "keepAliveSeconds"
* (all supporting updates at runtime); "poolSize", "activeCount" (for introspection only). * (all supporting updates at runtime); "poolSize", "activeCount" (for introspection only).
* *
* <p>For an alternative, you may set up a ThreadPoolExecutor instance directly using * <p>The default configuration is a core pool size of 1, with unlimited max pool size
* constructor injection, or use a factory method definition that points to the * and unlimited queue capacity. This is roughly equivalent to
* {@link java.util.concurrent.Executors} class. To expose such a raw Executor as a * {@link java.util.concurrent.Executors#newSingleThreadExecutor()}, sharing a single
* Spring {@link org.springframework.core.task.TaskExecutor}, simply wrap it with a * thread for all tasks. Setting {@link #setQueueCapacity "queueCapacity"} to 0 mimics
* {@link org.springframework.scheduling.concurrent.ConcurrentTaskExecutor} adapter. * {@link java.util.concurrent.Executors#newCachedThreadPool()}, with immediate scaling
* of threads in the pool to a potentially very high number. Consider also setting a
* {@link #setMaxPoolSize "maxPoolSize"} at that point, as well as possibly a higher
* {@link #setCorePoolSize "corePoolSize"} (see also the
* {@link #setAllowCoreThreadTimeOut "allowCoreThreadTimeOut"} mode of scaling).
* *
* <p><b>NOTE:</b> This class implements Spring's * <p><b>NOTE:</b> This class implements Spring's
* {@link org.springframework.core.task.TaskExecutor} interface as well as the * {@link org.springframework.core.task.TaskExecutor} interface as well as the
@ -61,13 +65,17 @@ import org.springframework.util.concurrent.ListenableFutureTask;
* exception handling follows the TaskExecutor contract rather than the Executor contract, * exception handling follows the TaskExecutor contract rather than the Executor contract,
* in particular regarding the {@link org.springframework.core.task.TaskRejectedException}. * in particular regarding the {@link org.springframework.core.task.TaskRejectedException}.
* *
* <p><b>If you prefer native {@link java.util.concurrent.ExecutorService} exposure instead, * <p>For an alternative, you may set up a ThreadPoolExecutor instance directly using
* consider {@link ThreadPoolExecutorFactoryBean} as an alternative to this class.</b> * constructor injection, or use a factory method definition that points to the
* {@link java.util.concurrent.Executors} class. To expose such a raw Executor as a
* Spring {@link org.springframework.core.task.TaskExecutor}, simply wrap it with a
* {@link org.springframework.scheduling.concurrent.ConcurrentTaskExecutor} adapter.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 2.0 * @since 2.0
* @see org.springframework.core.task.TaskExecutor * @see org.springframework.core.task.TaskExecutor
* @see java.util.concurrent.ThreadPoolExecutor * @see java.util.concurrent.ThreadPoolExecutor
* @see ThreadPoolExecutorFactoryBean
* @see ConcurrentTaskExecutor * @see ConcurrentTaskExecutor
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")

112
src/docs/asciidoc/integration.adoc

@ -463,7 +463,7 @@ shown in this example:
<entry key="/remoting/AccountService" value-ref="accountExporter"/> <entry key="/remoting/AccountService" value-ref="accountExporter"/>
</util:map> </util:map>
</property> </property>
<property name="port" value="8080" /> <property name="port" value="8080"/>
</bean> </bean>
---- ----
@ -2061,13 +2061,13 @@ containers that ships with Spring (in this case the `DefaultMessageListenerConta
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
<!-- this is the Message Driven POJO (MDP) --> <!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" /> <bean id="messageListener" class="jmsexample.ExampleListener"/>
<!-- and this is the message listener container --> <!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/> <property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/> <property name="destination" ref="destination"/>
**<property name="messageListener" ref="messageListener" />** **<property name="messageListener" ref="messageListener"/>**
</bean> </bean>
---- ----
@ -2163,7 +2163,7 @@ POJO that we will make into an MDP via the following configuration.
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/> <property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/> <property name="destination" ref="destination"/>
**<property name="messageListener" ref="messageListener" />** **<property name="messageListener" ref="messageListener"/>**
</bean> </bean>
---- ----
@ -5930,49 +5930,37 @@ behavior, it is possible to use this abstraction for your own needs.
==== TaskExecutor types ==== TaskExecutor types
There are a number of pre-built implementations of `TaskExecutor` included with the 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. Spring distribution. In all likelihood, you should never need to implement your own.
The common out-of-the-box variants are:
* `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` * `SimpleAsyncTaskExecutor`
This implementation does not reuse any threads, rather it starts up a new thread 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 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 any invocations that are over the limit until a slot has been freed up. If you
are looking for true pooling, see the discussions of `SimpleThreadPoolTaskExecutor` are looking for true pooling, see `ThreadPoolTaskExecutor` below.
and `ThreadPoolTaskExecutor` below.
* `SyncTaskExecutor`
This implementation doesn't execute invocations asynchronously. Instead, each
invocation takes place in the calling thread. It is primarily used in situations
where multi-threading isn't necessary such as simple test cases.
* `ConcurrentTaskExecutor` * `ConcurrentTaskExecutor`
This implementation is an adapter for a `java.util.concurrent.Executor` object. This implementation is an adapter for a `java.util.concurrent.Executor` instance.
There is an alternative, `ThreadPoolTaskExecutor`, that exposes the `Executor` There is an alternative, `ThreadPoolTaskExecutor`, that exposes the `Executor`
configuration parameters as bean properties. It is rare to need to use the configuration parameters as bean properties. There is rarely a need to use
`ConcurrentTaskExecutor`, but if the `ThreadPoolTaskExecutor` isn't flexible `ConcurrentTaskExecutor` directly, but if the `ThreadPoolTaskExecutor` is not
enough for your needs, the `ConcurrentTaskExecutor` is an alternative. flexible enough for your needs, then `ConcurrentTaskExecutor` is an alternative.
* `SimpleThreadPoolTaskExecutor`
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
thread pool that may need to be shared by both Quartz and non-Quartz components.
* `ThreadPoolTaskExecutor` * `ThreadPoolTaskExecutor`
This implementation is the most commonly used one. It exposes bean properties for This implementation is the most commonly used one. It exposes bean properties for
configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`. configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
If you need to adapt to a different kind of `java.util.concurrent.Executor`, it is If you need to adapt to a different kind of `java.util.concurrent.Executor`, it is
recommended that you use a `ConcurrentTaskExecutor` instead. recommended that you use a `ConcurrentTaskExecutor` instead.
* `WorkManagerTaskExecutor` * `WorkManagerTaskExecutor`
This implementation uses a CommonJ `WorkManager` as its backing service provider
+ and is the central convenience class for setting up CommonJ-based thread pool
integration on WebLogic/WebSphere within a Spring application context.
**** * `DefaultManagedTaskExecutor`
CommonJ is a set of specifications jointly developed between BEA and IBM. These This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
specifications are not Java EE standards, but are standard across BEA's and IBM's compatible runtime environment such as a Java EE 7+ application server,
Application Server implementations. replacing a CommonJ WorkManager for that purpose.
****
+
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.
[[scheduling-task-executor-usage]] [[scheduling-task-executor-usage]]
@ -6000,7 +5988,6 @@ out a set of messages.
public void run() { public void run() {
System.out.println(message); System.out.println(message);
} }
} }
private TaskExecutor taskExecutor; private TaskExecutor taskExecutor;
@ -6014,7 +6001,6 @@ out a set of messages.
taskExecutor.execute(new MessagePrinterTask("Message" + i)); taskExecutor.execute(new MessagePrinterTask("Message" + i));
} }
} }
} }
---- ----
@ -6029,13 +6015,13 @@ been exposed.
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" /> <property name="corePoolSize" value="5"/>
<property name="maxPoolSize" value="10" /> <property name="maxPoolSize" value="10"/>
<property name="queueCapacity" value="25" /> <property name="queueCapacity" value="25"/>
</bean> </bean>
<bean id="taskExecutorExample" class="TaskExecutorExample"> <bean id="taskExecutorExample" class="TaskExecutorExample">
<constructor-arg ref="taskExecutor" /> <constructor-arg ref="taskExecutor"/>
</bean> </bean>
---- ----
@ -6054,16 +6040,25 @@ with a variety of methods for scheduling tasks to run at some point in the futur
ScheduledFuture schedule(Runnable task, Trigger trigger); ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Instant startTime);
ScheduledFuture schedule(Runnable task, Date startTime); ScheduledFuture schedule(Runnable task, Date startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, long period); ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
} }
---- ----
@ -6077,8 +6072,8 @@ much more flexible.
[[scheduling-trigger-interface]] [[scheduling-trigger-interface]]
==== Trigger interface ==== Trigger interface
The `Trigger` interface is essentially inspired by JSR-236, which, as of Spring 3.0, has The `Trigger` interface is essentially inspired by JSR-236 which, as of Spring 3.0,
not yet been officially implemented. The basic idea of the `Trigger` is that execution was not yet officially implemented. The basic idea of the `Trigger` is that execution
times may be determined based on past execution outcomes or even arbitrary conditions. times may be determined based on past execution outcomes or even arbitrary conditions.
If these determinations do take into account the outcome of the preceding execution, If these determinations do take into account the outcome of the preceding execution,
that information is available within a `TriggerContext`. The `Trigger` interface itself that information is available within a `TriggerContext`. The `Trigger` interface itself
@ -6090,7 +6085,6 @@ is quite simple:
public interface Trigger { public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext); Date nextExecutionTime(TriggerContext triggerContext);
} }
---- ----
@ -6109,7 +6103,6 @@ default). Here you can see what methods are available for `Trigger` implementati
Date lastActualExecutionTime(); Date lastActualExecutionTime();
Date lastCompletionTime(); Date lastCompletionTime();
} }
---- ----
@ -6144,19 +6137,21 @@ could be configured externally and therefore easily modified or extended.
==== TaskScheduler implementations ==== TaskScheduler implementations
As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler` As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler`
is that code relying on scheduling behavior need not be coupled to a particular arrangement is that an application's scheduling needs are decoupled from the deployment
scheduler implementation. The flexibility this provides is particularly relevant when environment. This abstraction level is particularly relevant when deploying to an
running within Application Server environments where threads should not be created application server environment where threads should not be created directly by the
directly by the application itself. For such cases, Spring provides a application itself. For such scenarios, Spring provides a `TimerManagerTaskScheduler`
`TimerManagerTaskScheduler` that delegates to a CommonJ TimerManager instance, typically delegating to a CommonJ TimerManager on WebLogic/WebSphere as well as a more recent
configured with a JNDI-lookup. `DefaultManagedTaskScheduler` delegating to a JSR-236 `ManagedScheduledExecutorService`
in a Java EE 7+ environment, both typically configured with a JNDI lookup.
A simpler alternative, the `ThreadPoolTaskScheduler`, can be used whenever external Whenever external thread management is not a requirement, a simpler alternative is
thread management is not a requirement. Internally, it delegates to a a local `ScheduledExecutorService` setup within the application which can be adapted
`ScheduledExecutorService` instance. `ThreadPoolTaskScheduler` actually implements through Spring's `ConcurrentTaskScheduler`. As a convenience, Spring also provides a
Spring's `TaskExecutor` interface as well, so that a single instance can be used for `ThreadPoolTaskScheduler` which internally delegates to a `ScheduledExecutorService`,
asynchronous execution __as soon as possible__ as well as scheduled, and potentially providing common bean-style configuration along the lines of `ThreadPoolTaskExecutor`.
recurring, executions. These variants work perfectly fine for locally embedded thread pool setups in lenient
application server environments as well, in particular on Tomcat and Jetty.
@ -7377,8 +7372,7 @@ Alternatively for XML configuration use the `cache:annotation-driven` element:
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven /> <cache:annotation-driven/>
</beans> </beans>
---- ----

Loading…
Cancel
Save