|
|
|
|
@ -383,6 +383,139 @@ public class TaskExecutorExample {
@@ -383,6 +383,139 @@ public class TaskExecutorExample {
|
|
|
|
|
as scheduled, and potentially recurring, executions.</para> |
|
|
|
|
</section> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
<section id="scheduling-task-namespace"> |
|
|
|
|
<title>The Task Namespace</title> |
|
|
|
|
|
|
|
|
|
<para>Beginning with Spring 3.0, there is an XML namespace for configuring |
|
|
|
|
<interfacename>TaskExecutor</interfacename> and |
|
|
|
|
<interfacename>TaskScheduler</interfacename> instances. It also provides a |
|
|
|
|
convenient way to configure tasks to be scheduled with a trigger.</para> |
|
|
|
|
|
|
|
|
|
<section id="scheduling-task-namespace-scheduler"> |
|
|
|
|
<title>The 'scheduler' element</title> |
|
|
|
|
<para>The following element will create a |
|
|
|
|
<classname>ThreadPoolTaskScheduler</classname> instance with the |
|
|
|
|
specified thread pool size.</para> |
|
|
|
|
<programlisting language="xml"><![CDATA[<task:scheduler id="scheduler" size="10"/>]]></programlisting> |
|
|
|
|
|
|
|
|
|
<para>The value provided for the "id" attribute will be used as the |
|
|
|
|
prefix for thread names within the pool. The 'scheduler' element is |
|
|
|
|
relatively straightforward. If you do not provide a 'size' attribute, |
|
|
|
|
the default thread pool will only have a single thread. There are no |
|
|
|
|
other configuration options for the scheduler.</para> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
<section id="scheduling-task-namespace-executor"> |
|
|
|
|
<title>The 'executor' element</title> |
|
|
|
|
<para>The following will create a |
|
|
|
|
<classname>ThreadPoolTaskExecutor</classname> instance: |
|
|
|
|
<programlisting language="xml"><![CDATA[<task:executor id="executor" size="10"/>]]></programlisting> |
|
|
|
|
</para> |
|
|
|
|
|
|
|
|
|
<para>As with the scheduler above, the value provided for the "id" |
|
|
|
|
attribute will be used as the prefix for thread names within the pool. |
|
|
|
|
As far as the pool size is concerned, the 'executor' element supports |
|
|
|
|
more configuration options than the 'scheduler' element. For one thing, |
|
|
|
|
the thread pool for a <classname>ThreadPoolTaskExecutor</classname> is |
|
|
|
|
itself more configurable. Rather than just a single size, an executor's |
|
|
|
|
thread pool may have different values for the <emphasis>core</emphasis> |
|
|
|
|
and the <emphasis>max</emphasis> size. If a single value is provided |
|
|
|
|
then the executor will have a fixed-size thread pool (the core and max |
|
|
|
|
sizes are the same). However, the 'executor' element's 'size' attribute |
|
|
|
|
also accepts a range in the form of "m-n". |
|
|
|
|
<programlisting language="xml"><![CDATA[<task:executor id="executorWithPoolSizeRange" |
|
|
|
|
size="5-25" |
|
|
|
|
queue-capacity="100"/>]]></programlisting> |
|
|
|
|
</para> |
|
|
|
|
|
|
|
|
|
<para>As you can see from that configuration, a 'queue-capacity' value |
|
|
|
|
has also been provided. The configuration of the thread pool should |
|
|
|
|
also be considered in light of the executor's queue capacity. For the |
|
|
|
|
full description of the relationship between pool size and queue |
|
|
|
|
capacity, consult the documentation for |
|
|
|
|
<ulink url="http://java.sun.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html">ThreadPoolExecutor</ulink>. |
|
|
|
|
The main idea is that when a task is submitted, the executor will first |
|
|
|
|
try to use a free thread if the number of active threads is currently |
|
|
|
|
less than the core size. If the core size has been reached, then the |
|
|
|
|
task will be added to the queue as long as its capacity has not yet |
|
|
|
|
been reached. Only then, if the queue's capacity |
|
|
|
|
<emphasis>has</emphasis> been reached, will the executor create a new |
|
|
|
|
thread beyond the core size. If the max size has also been reached, |
|
|
|
|
then the executor will reject the task.</para> |
|
|
|
|
|
|
|
|
|
<para>By default, the queue is <emphasis>unbounded</emphasis>, but this |
|
|
|
|
is rarely the desired configuration, because it can lead to |
|
|
|
|
<classname>OutOfMemoryErrors</classname> if enough tasks are added to |
|
|
|
|
that queue while all pool threads are busy. Furthermore, if the queue |
|
|
|
|
is unbounded, then the max size has no effect at all. Since the |
|
|
|
|
executor will always try the queue before creating a new thread beyond |
|
|
|
|
the core size, a queue must have a finite capacity for the thread pool |
|
|
|
|
to grow beyond the core size (this is why a "fixed-size" pool is the |
|
|
|
|
only sensible case when using an unbounded queue).</para> |
|
|
|
|
|
|
|
|
|
<para>In a moment, we will review the effects of the keep-alive setting |
|
|
|
|
which adds yet another factor to consider when providing a pool size |
|
|
|
|
configuration. First, let's consider the case, as mentioned above, when |
|
|
|
|
a task is rejected. By default, when a task is rejected, a thread pool |
|
|
|
|
executor will throw a <classname>TaskRejectedException</classname>. |
|
|
|
|
However, the rejection policy is actually configurable. The exception |
|
|
|
|
is thrown when using the default rejection policy which is the |
|
|
|
|
<classname>AbortPolicy</classname> implementation. For applications |
|
|
|
|
where some tasks can be skipped under heavy load, either the |
|
|
|
|
<classname>DiscardPolicy</classname> or |
|
|
|
|
<classname>DiscardOldestPolicy</classname> may be configured instead. |
|
|
|
|
Another option that works well for applications that need to throttle |
|
|
|
|
the submitted tasks under heavy load is the |
|
|
|
|
<classname>CallerRunsPolicy</classname>. Instead of throwing an |
|
|
|
|
exception or discarding tasks, that policy will simply force the thread |
|
|
|
|
that is calling the submit method to run the task itself. The idea is |
|
|
|
|
that such a caller will be busy while running that task and not able to |
|
|
|
|
submit other tasks immediately. Therefore it provides a simple way to |
|
|
|
|
throttle the incoming load while maintaining the limits of the thread |
|
|
|
|
pool and queue. Typically this allows the executor to "catch up" on the |
|
|
|
|
tasks it is handling and thereby frees up some capacity on the queue, |
|
|
|
|
in the pool, or both. Any of these options can be chosen from an |
|
|
|
|
enumeration of values available for the 'rejection-policy' attribute on |
|
|
|
|
the 'executor' element.</para> |
|
|
|
|
<programlisting language="xml"><![CDATA[<task:executor id="executorWithCallerRunsPolicy" |
|
|
|
|
size="5-25" |
|
|
|
|
queue-capacity="100" |
|
|
|
|
rejection-policy="CALLER_RUNS"/>]]></programlisting> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
<section id="scheduling-task-namespace-scheduled-tasks"> |
|
|
|
|
<title>The 'scheduled-tasks' element</title> |
|
|
|
|
|
|
|
|
|
<para>The most powerful feature of Spring's task namespace is the |
|
|
|
|
support for configuring tasks to be scheduled within a Spring |
|
|
|
|
Application Context. This follows an approach similar to other |
|
|
|
|
"method-invokers" in Spring, such as that provided by the JMS namespace |
|
|
|
|
for configuring Message-driven POJOs. Basically a "ref" attribute can |
|
|
|
|
point to any Spring-managed object, and the "method" attribute provides |
|
|
|
|
the name of a method to be invoked on that object. Here is a simple |
|
|
|
|
example.</para> |
|
|
|
|
<programlisting language="xml"><![CDATA[<task:scheduled-tasks scheduler="myScheduler"> |
|
|
|
|
<task:scheduled ref="someObject" method="someMethod" fixed-delay="5000"/> |
|
|
|
|
<task:scheduled-tasks/> |
|
|
|
|
|
|
|
|
|
<task:scheduler id="myScheduler" size="10"/>]]></programlisting> |
|
|
|
|
|
|
|
|
|
<para>As you can see, the scheduler is referenced by the outer element, |
|
|
|
|
and each individual task includes the configuration of its trigger |
|
|
|
|
metadata. In the preceding example, that metadata defines a periodic |
|
|
|
|
trigger with a fixed delay. It could also be configured with a |
|
|
|
|
"fixed-rate", or for more control, a "cron" attribute could be provided |
|
|
|
|
instead. Here's an example featuring these other options.</para> |
|
|
|
|
<programlisting language="xml"><![CDATA[<task:scheduled-tasks scheduler="myScheduler"> |
|
|
|
|
<task:scheduled ref="someObject" method="someMethod" fixed-rate="5000"/> |
|
|
|
|
<task:scheduled ref="anotherObject" method="anotherMethod" cron="*/5 * * * * MON-FRI"/> |
|
|
|
|
<task:scheduled-tasks/> |
|
|
|
|
|
|
|
|
|
<task:scheduler id="myScheduler" size="10"/>]]></programlisting> |
|
|
|
|
</section> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
<section id="scheduling-quartz"> |
|
|
|
|
<title>Using the OpenSymphony Quartz Scheduler</title> |
|
|
|
|
<para>Quartz uses <classname>Trigger</classname>, <classname>Job</classname> and |
|
|
|
|
|