001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.core.task;
018:
019: import java.io.Serializable;
020:
021: import org.springframework.util.Assert;
022: import org.springframework.util.ClassUtils;
023: import org.springframework.util.ConcurrencyThrottleSupport;
024: import org.springframework.util.CustomizableThreadCreator;
025:
026: /**
027: * TaskExecutor implementation that fires up a new Thread for each task,
028: * executing it asynchronously.
029: *
030: * <p>Supports limiting concurrent threads through the "concurrencyLimit"
031: * bean property. By default, the number of concurrent threads is unlimited.
032: *
033: * <p><b>NOTE: This implementation does not reuse threads!</b> Consider a
034: * thread-pooling TaskExecutor implementation instead, in particular for
035: * executing a large number of short-lived tasks.
036: *
037: * @author Juergen Hoeller
038: * @since 2.0
039: * @see #setConcurrencyLimit
040: * @see SyncTaskExecutor
041: * @see org.springframework.scheduling.timer.TimerTaskExecutor
042: * @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
043: * @see org.springframework.scheduling.commonj.WorkManagerTaskExecutor
044: */
045: public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
046: implements AsyncTaskExecutor, Serializable {
047:
048: /**
049: * Default thread name prefix: "SimpleAsyncTaskExecutor-".
050: * @deprecated as of Spring 2.0.3, since the default thread name prefix
051: * is now taken from the concrete class (could be a subclass)
052: */
053: public static final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils
054: .getShortName(SimpleAsyncTaskExecutor.class)
055: + "-";
056:
057: /**
058: * Permit any number of concurrent invocations: that is, don't throttle concurrency.
059: */
060: public static final int UNBOUNDED_CONCURRENCY = ConcurrencyThrottleSupport.UNBOUNDED_CONCURRENCY;
061:
062: /**
063: * Switch concurrency 'off': that is, don't allow any concurrent invocations.
064: */
065: public static final int NO_CONCURRENCY = ConcurrencyThrottleSupport.NO_CONCURRENCY;
066:
067: /**
068: * Internal concurrency throttle used by this executor.
069: */
070: private final ConcurrencyThrottleAdapter concurrencyThrottle = new ConcurrencyThrottleAdapter();
071:
072: /**
073: * Create a new SimpleAsyncTaskExecutor with default thread name prefix.
074: */
075: public SimpleAsyncTaskExecutor() {
076: super ();
077: }
078:
079: /**
080: * Create a new SimpleAsyncTaskExecutor with the given thread name prefix.
081: * @param threadNamePrefix the prefix to use for the names of newly created threads
082: */
083: public SimpleAsyncTaskExecutor(String threadNamePrefix) {
084: super (threadNamePrefix);
085: }
086:
087: /**
088: * Set the maximum number of parallel accesses allowed.
089: * -1 indicates no concurrency limit at all.
090: * <p>In principle, this limit can be changed at runtime,
091: * although it is generally designed as a config time setting.
092: * NOTE: Do not switch between -1 and any concrete limit at runtime,
093: * as this will lead to inconsistent concurrency counts: A limit
094: * of -1 effectively turns off concurrency counting completely.
095: * @see #UNBOUNDED_CONCURRENCY
096: */
097: public void setConcurrencyLimit(int concurrencyLimit) {
098: this .concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);
099: }
100:
101: /**
102: * Return the maximum number of parallel accesses allowed.
103: */
104: public int getConcurrencyLimit() {
105: return this .concurrencyThrottle.getConcurrencyLimit();
106: }
107:
108: /**
109: * Return whether this throttle is currently active.
110: * @return <code>true</code> if the concurrency limit for this instance is active
111: * @see #getConcurrencyLimit()
112: * @see #setConcurrencyLimit
113: */
114: public boolean isThrottleActive() {
115: return this .concurrencyThrottle.isThrottleActive();
116: }
117:
118: /**
119: * Executes the given task, within a concurrency throttle
120: * if configured (through the superclass's settings).
121: * @see #doExecute(Runnable)
122: */
123: public void execute(Runnable task) {
124: execute(task, TIMEOUT_INDEFINITE);
125: }
126:
127: /**
128: * Executes the given task, within a concurrency throttle
129: * if configured (through the superclass's settings).
130: * <p>Executes urgent tasks (with 'immediate' timeout) directly,
131: * bypassing the concurrency throttle (if active). All other
132: * tasks are subject to throttling.
133: * @see #TIMEOUT_IMMEDIATE
134: * @see #doExecute(Runnable)
135: */
136: public void execute(Runnable task, long startTimeout) {
137: Assert.notNull(task, "Runnable must not be null");
138: if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
139: this .concurrencyThrottle.beforeAccess();
140: doExecute(new ConcurrencyThrottlingRunnable(task));
141: } else {
142: doExecute(task);
143: }
144: }
145:
146: /**
147: * Template method for the actual execution of a task.
148: * <p>The default implementation creates a new Thread and starts it.
149: * @param task the Runnable to execute
150: * @see #createThread
151: * @see java.lang.Thread#start()
152: */
153: protected void doExecute(Runnable task) {
154: createThread(task).start();
155: }
156:
157: /**
158: * Subclass of the general ConcurrencyThrottleSupport class,
159: * making <code>beforeAccess()</code> and <code>afterAccess()</code>
160: * visible to the surrounding class.
161: */
162: private static class ConcurrencyThrottleAdapter extends
163: ConcurrencyThrottleSupport {
164:
165: protected void beforeAccess() {
166: super .beforeAccess();
167: }
168:
169: protected void afterAccess() {
170: super .afterAccess();
171: }
172: }
173:
174: /**
175: * This Runnable calls <code>afterAccess()</code> after the
176: * target Runnable has finished its execution.
177: */
178: private class ConcurrencyThrottlingRunnable implements Runnable {
179:
180: private final Runnable target;
181:
182: public ConcurrencyThrottlingRunnable(Runnable target) {
183: this .target = target;
184: }
185:
186: public void run() {
187: try {
188: this.target.run();
189: } finally {
190: concurrencyThrottle.afterAccess();
191: }
192: }
193: }
194:
195: }
|