Skip to content

Commit 5a11112

Browse files
committed
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)
1 parent ac48c64 commit 5a11112

File tree

4 files changed

+84
-67
lines changed

4 files changed

+84
-67
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@
7676
* method.</li>
7777
* </ul>
7878
*
79+
* <p><b>NOTE: {@link AsyncConfigurer} configuration classes get initialized early
80+
* in the application context bootstrap. If you need any dependencies on other beans
81+
* there, make sure to declare them 'lazy' as far as possible in order to let them
82+
* go through other post-processors as well.</b>
83+
*
7984
* <pre class="code">
8085
* &#064;Configuration
8186
* &#064;EnableAsync

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,6 +37,16 @@
3737
* "queueCapacity" properties) and exposing it as a bean reference of its native
3838
* {@link java.util.concurrent.ExecutorService} type.
3939
*
40+
* <p>The default configuration is a core pool size of 1, with unlimited max pool size
41+
* and unlimited queue capacity. This is roughly equivalent to
42+
* {@link java.util.concurrent.Executors#newSingleThreadExecutor()}, sharing a single
43+
* thread for all tasks. Setting {@link #setQueueCapacity "queueCapacity"} to 0 mimics
44+
* {@link java.util.concurrent.Executors#newCachedThreadPool()}, with immediate scaling
45+
* of threads in the pool to a potentially very high number. Consider also setting a
46+
* {@link #setMaxPoolSize "maxPoolSize"} at that point, as well as possibly a higher
47+
* {@link #setCorePoolSize "corePoolSize"} (see also the
48+
* {@link #setAllowCoreThreadTimeOut "allowCoreThreadTimeOut"} mode of scaling).
49+
*
4050
* <p>For an alternative, you may set up a {@link ThreadPoolExecutor} instance directly
4151
* using constructor injection, or use a factory method definition that points to the
4252
* {@link java.util.concurrent.Executors} class.

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,15 @@
4848
* providing several useful attributes: "corePoolSize", "maxPoolSize", "keepAliveSeconds"
4949
* (all supporting updates at runtime); "poolSize", "activeCount" (for introspection only).
5050
*
51-
* <p>For an alternative, you may set up a ThreadPoolExecutor instance directly using
52-
* constructor injection, or use a factory method definition that points to the
53-
* {@link java.util.concurrent.Executors} class. To expose such a raw Executor as a
54-
* Spring {@link org.springframework.core.task.TaskExecutor}, simply wrap it with a
55-
* {@link org.springframework.scheduling.concurrent.ConcurrentTaskExecutor} adapter.
51+
* <p>The default configuration is a core pool size of 1, with unlimited max pool size
52+
* and unlimited queue capacity. This is roughly equivalent to
53+
* {@link java.util.concurrent.Executors#newSingleThreadExecutor()}, sharing a single
54+
* thread for all tasks. Setting {@link #setQueueCapacity "queueCapacity"} to 0 mimics
55+
* {@link java.util.concurrent.Executors#newCachedThreadPool()}, with immediate scaling
56+
* of threads in the pool to a potentially very high number. Consider also setting a
57+
* {@link #setMaxPoolSize "maxPoolSize"} at that point, as well as possibly a higher
58+
* {@link #setCorePoolSize "corePoolSize"} (see also the
59+
* {@link #setAllowCoreThreadTimeOut "allowCoreThreadTimeOut"} mode of scaling).
5660
*
5761
* <p><b>NOTE:</b> This class implements Spring's
5862
* {@link org.springframework.core.task.TaskExecutor} interface as well as the
@@ -61,13 +65,17 @@
6165
* exception handling follows the TaskExecutor contract rather than the Executor contract,
6266
* in particular regarding the {@link org.springframework.core.task.TaskRejectedException}.
6367
*
64-
* <p><b>If you prefer native {@link java.util.concurrent.ExecutorService} exposure instead,
65-
* consider {@link ThreadPoolExecutorFactoryBean} as an alternative to this class.</b>
68+
* <p>For an alternative, you may set up a ThreadPoolExecutor instance directly using
69+
* constructor injection, or use a factory method definition that points to the
70+
* {@link java.util.concurrent.Executors} class. To expose such a raw Executor as a
71+
* Spring {@link org.springframework.core.task.TaskExecutor}, simply wrap it with a
72+
* {@link org.springframework.scheduling.concurrent.ConcurrentTaskExecutor} adapter.
6673
*
6774
* @author Juergen Hoeller
6875
* @since 2.0
6976
* @see org.springframework.core.task.TaskExecutor
7077
* @see java.util.concurrent.ThreadPoolExecutor
78+
* @see ThreadPoolExecutorFactoryBean
7179
* @see ConcurrentTaskExecutor
7280
*/
7381
@SuppressWarnings("serial")

src/docs/asciidoc/integration.adoc

Lines changed: 53 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ shown in this example:
463463
<entry key="/remoting/AccountService" value-ref="accountExporter"/>
464464
</util:map>
465465
</property>
466-
<property name="port" value="8080" />
466+
<property name="port" value="8080"/>
467467
</bean>
468468
----
469469

@@ -2061,13 +2061,13 @@ containers that ships with Spring (in this case the `DefaultMessageListenerConta
20612061
[subs="verbatim,quotes"]
20622062
----
20632063
<!-- this is the Message Driven POJO (MDP) -->
2064-
<bean id="messageListener" class="jmsexample.ExampleListener" />
2064+
<bean id="messageListener" class="jmsexample.ExampleListener"/>
20652065
20662066
<!-- and this is the message listener container -->
20672067
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
20682068
<property name="connectionFactory" ref="connectionFactory"/>
20692069
<property name="destination" ref="destination"/>
2070-
**<property name="messageListener" ref="messageListener" />**
2070+
**<property name="messageListener" ref="messageListener"/>**
20712071
</bean>
20722072
----
20732073

@@ -2163,7 +2163,7 @@ POJO that we will make into an MDP via the following configuration.
21632163
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
21642164
<property name="connectionFactory" ref="connectionFactory"/>
21652165
<property name="destination" ref="destination"/>
2166-
**<property name="messageListener" ref="messageListener" />**
2166+
**<property name="messageListener" ref="messageListener"/>**
21672167
</bean>
21682168
----
21692169

@@ -5930,49 +5930,37 @@ behavior, it is possible to use this abstraction for your own needs.
59305930
==== TaskExecutor types
59315931

59325932
There are a number of pre-built implementations of `TaskExecutor` included with the
5933-
Spring distribution. In all likelihood, you shouldn't ever need to implement your own.
5933+
Spring distribution. In all likelihood, you should never need to implement your own.
5934+
The common out-of-the-box variants are:
59345935

5936+
* `SyncTaskExecutor`
5937+
This implementation does not execute invocations asynchronously. Instead, each
5938+
invocation takes place in the calling thread. It is primarily used in situations
5939+
where multi-threading is not necessary such as in simple test cases.
59355940
* `SimpleAsyncTaskExecutor`
59365941
This implementation does not reuse any threads, rather it starts up a new thread
59375942
for each invocation. However, it does support a concurrency limit which will block
59385943
any invocations that are over the limit until a slot has been freed up. If you
5939-
are looking for true pooling, see the discussions of `SimpleThreadPoolTaskExecutor`
5940-
and `ThreadPoolTaskExecutor` below.
5941-
* `SyncTaskExecutor`
5942-
This implementation doesn't execute invocations asynchronously. Instead, each
5943-
invocation takes place in the calling thread. It is primarily used in situations
5944-
where multi-threading isn't necessary such as simple test cases.
5944+
are looking for true pooling, see `ThreadPoolTaskExecutor` below.
59455945
* `ConcurrentTaskExecutor`
5946-
This implementation is an adapter for a `java.util.concurrent.Executor` object.
5946+
This implementation is an adapter for a `java.util.concurrent.Executor` instance.
59475947
There is an alternative, `ThreadPoolTaskExecutor`, that exposes the `Executor`
5948-
configuration parameters as bean properties. It is rare to need to use the
5949-
`ConcurrentTaskExecutor`, but if the `ThreadPoolTaskExecutor` isn't flexible
5950-
enough for your needs, the `ConcurrentTaskExecutor` is an alternative.
5951-
* `SimpleThreadPoolTaskExecutor`
5952-
This implementation is actually a subclass of Quartz's `SimpleThreadPool` which
5953-
listens to Spring's lifecycle callbacks. This is typically used when you have a
5954-
thread pool that may need to be shared by both Quartz and non-Quartz components.
5948+
configuration parameters as bean properties. There is rarely a need to use
5949+
`ConcurrentTaskExecutor` directly, but if the `ThreadPoolTaskExecutor` is not
5950+
flexible enough for your needs, then `ConcurrentTaskExecutor` is an alternative.
59555951
* `ThreadPoolTaskExecutor`
59565952
This implementation is the most commonly used one. It exposes bean properties for
59575953
configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
59585954
If you need to adapt to a different kind of `java.util.concurrent.Executor`, it is
59595955
recommended that you use a `ConcurrentTaskExecutor` instead.
59605956
* `WorkManagerTaskExecutor`
5961-
5962-
+
5963-
5964-
****
5965-
CommonJ is a set of specifications jointly developed between BEA and IBM. These
5966-
specifications are not Java EE standards, but are standard across BEA's and IBM's
5967-
Application Server implementations.
5968-
****
5969-
5970-
+
5971-
5972-
This implementation uses the CommonJ `WorkManager` as its backing implementation and is
5973-
the central convenience class for setting up a CommonJ `WorkManager` reference in a Spring
5974-
context. Similar to the `SimpleThreadPoolTaskExecutor`, this class implements the
5975-
`WorkManager` interface and therefore can be used directly as a `WorkManager` as well.
5957+
This implementation uses a CommonJ `WorkManager` as its backing service provider
5958+
and is the central convenience class for setting up CommonJ-based thread pool
5959+
integration on WebLogic/WebSphere within a Spring application context.
5960+
* `DefaultManagedTaskExecutor`
5961+
This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
5962+
compatible runtime environment such as a Java EE 7+ application server,
5963+
replacing a CommonJ WorkManager for that purpose.
59765964

59775965

59785966
[[scheduling-task-executor-usage]]
@@ -6000,7 +5988,6 @@ out a set of messages.
60005988
public void run() {
60015989
System.out.println(message);
60025990
}
6003-
60045991
}
60055992
60065993
private TaskExecutor taskExecutor;
@@ -6014,7 +6001,6 @@ out a set of messages.
60146001
taskExecutor.execute(new MessagePrinterTask("Message" + i));
60156002
}
60166003
}
6017-
60186004
}
60196005
----
60206006

@@ -6029,13 +6015,13 @@ been exposed.
60296015
[subs="verbatim,quotes"]
60306016
----
60316017
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
6032-
<property name="corePoolSize" value="5" />
6033-
<property name="maxPoolSize" value="10" />
6034-
<property name="queueCapacity" value="25" />
6018+
<property name="corePoolSize" value="5"/>
6019+
<property name="maxPoolSize" value="10"/>
6020+
<property name="queueCapacity" value="25"/>
60356021
</bean>
60366022
60376023
<bean id="taskExecutorExample" class="TaskExecutorExample">
6038-
<constructor-arg ref="taskExecutor" />
6024+
<constructor-arg ref="taskExecutor"/>
60396025
</bean>
60406026
----
60416027

@@ -6054,16 +6040,25 @@ with a variety of methods for scheduling tasks to run at some point in the futur
60546040
60556041
ScheduledFuture schedule(Runnable task, Trigger trigger);
60566042
6043+
ScheduledFuture schedule(Runnable task, Instant startTime);
6044+
60576045
ScheduledFuture schedule(Runnable task, Date startTime);
60586046
6047+
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
6048+
60596049
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
60606050
6051+
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
6052+
60616053
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
60626054
6055+
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
6056+
60636057
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
60646058
6065-
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
6059+
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
60666060
6061+
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
60676062
}
60686063
----
60696064

@@ -6077,8 +6072,8 @@ much more flexible.
60776072
[[scheduling-trigger-interface]]
60786073
==== Trigger interface
60796074

6080-
The `Trigger` interface is essentially inspired by JSR-236, which, as of Spring 3.0, has
6081-
not yet been officially implemented. The basic idea of the `Trigger` is that execution
6075+
The `Trigger` interface is essentially inspired by JSR-236 which, as of Spring 3.0,
6076+
was not yet officially implemented. The basic idea of the `Trigger` is that execution
60826077
times may be determined based on past execution outcomes or even arbitrary conditions.
60836078
If these determinations do take into account the outcome of the preceding execution,
60846079
that information is available within a `TriggerContext`. The `Trigger` interface itself
@@ -6090,7 +6085,6 @@ is quite simple:
60906085
public interface Trigger {
60916086
60926087
Date nextExecutionTime(TriggerContext triggerContext);
6093-
60946088
}
60956089
----
60966090

@@ -6109,7 +6103,6 @@ default). Here you can see what methods are available for `Trigger` implementati
61096103
Date lastActualExecutionTime();
61106104
61116105
Date lastCompletionTime();
6112-
61136106
}
61146107
----
61156108

@@ -6144,19 +6137,21 @@ could be configured externally and therefore easily modified or extended.
61446137
==== TaskScheduler implementations
61456138

61466139
As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler`
6147-
is that code relying on scheduling behavior need not be coupled to a particular
6148-
scheduler implementation. The flexibility this provides is particularly relevant when
6149-
running within Application Server environments where threads should not be created
6150-
directly by the application itself. For such cases, Spring provides a
6151-
`TimerManagerTaskScheduler` that delegates to a CommonJ TimerManager instance, typically
6152-
configured with a JNDI-lookup.
6140+
arrangement is that an application's scheduling needs are decoupled from the deployment
6141+
environment. This abstraction level is particularly relevant when deploying to an
6142+
application server environment where threads should not be created directly by the
6143+
application itself. For such scenarios, Spring provides a `TimerManagerTaskScheduler`
6144+
delegating to a CommonJ TimerManager on WebLogic/WebSphere as well as a more recent
6145+
`DefaultManagedTaskScheduler` delegating to a JSR-236 `ManagedScheduledExecutorService`
6146+
in a Java EE 7+ environment, both typically configured with a JNDI lookup.
61536147

6154-
A simpler alternative, the `ThreadPoolTaskScheduler`, can be used whenever external
6155-
thread management is not a requirement. Internally, it delegates to a
6156-
`ScheduledExecutorService` instance. `ThreadPoolTaskScheduler` actually implements
6157-
Spring's `TaskExecutor` interface as well, so that a single instance can be used for
6158-
asynchronous execution __as soon as possible__ as well as scheduled, and potentially
6159-
recurring, executions.
6148+
Whenever external thread management is not a requirement, a simpler alternative is
6149+
a local `ScheduledExecutorService` setup within the application which can be adapted
6150+
through Spring's `ConcurrentTaskScheduler`. As a convenience, Spring also provides a
6151+
`ThreadPoolTaskScheduler` which internally delegates to a `ScheduledExecutorService`,
6152+
providing common bean-style configuration along the lines of `ThreadPoolTaskExecutor`.
6153+
These variants work perfectly fine for locally embedded thread pool setups in lenient
6154+
application server environments as well, in particular on Tomcat and Jetty.
61606155

61616156

61626157

@@ -7377,8 +7372,7 @@ Alternatively for XML configuration use the `cache:annotation-driven` element:
73777372
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
73787373
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
73797374
7380-
<cache:annotation-driven />
7381-
7375+
<cache:annotation-driven/>
73827376
</beans>
73837377
----
73847378

0 commit comments

Comments
 (0)