/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.concurrent;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;

JavaBean that allows for configuring a ThreadPoolExecutor in bean style (through its "corePoolSize", "maxPoolSize", "keepAliveSeconds", "queueCapacity" properties) and exposing it as a bean reference of its native ExecutorService type.

The default configuration is a core pool size of 1, with unlimited max pool size and unlimited queue capacity. This is roughly equivalent to Executors.newSingleThreadExecutor(), sharing a single thread for all tasks. Setting "queueCapacity" to 0 mimics Executors.newCachedThreadPool(), with immediate scaling of threads in the pool to a potentially very high number. Consider also setting a "maxPoolSize" at that point, as well as possibly a higher "corePoolSize" (see also the "allowCoreThreadTimeOut" mode of scaling).

For an alternative, you may set up a ThreadPoolExecutor instance directly using constructor injection, or use a factory method definition that points to the Executors class. This is strongly recommended in particular for common @Bean methods in configuration classes, where this FactoryBean variant would force you to return the FactoryBean type instead of the actual Executor type.

If you need a timing-based ScheduledExecutorService instead, consider ScheduledExecutorFactoryBean.

Author:Juergen Hoeller
See Also:
Since:3.0
/** * JavaBean that allows for configuring a {@link java.util.concurrent.ThreadPoolExecutor} * in bean style (through its "corePoolSize", "maxPoolSize", "keepAliveSeconds", * "queueCapacity" properties) and exposing it as a bean reference of its native * {@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 * using constructor injection, or use a factory method definition that points to the * {@link java.util.concurrent.Executors} class. * <b>This is strongly recommended in particular for common {@code @Bean} methods in * configuration classes, where this {@code FactoryBean} variant would force you to * return the {@code FactoryBean} type instead of the actual {@code Executor} type.</b> * * <p>If you need a timing-based {@link java.util.concurrent.ScheduledExecutorService} * instead, consider {@link ScheduledExecutorFactoryBean}. * @author Juergen Hoeller * @since 3.0 * @see java.util.concurrent.ExecutorService * @see java.util.concurrent.Executors * @see java.util.concurrent.ThreadPoolExecutor */
@SuppressWarnings("serial") public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport implements FactoryBean<ExecutorService>, InitializingBean, DisposableBean { private int corePoolSize = 1; private int maxPoolSize = Integer.MAX_VALUE; private int keepAliveSeconds = 60; private boolean allowCoreThreadTimeOut = false; private int queueCapacity = Integer.MAX_VALUE; private boolean exposeUnconfigurableExecutor = false; @Nullable private ExecutorService exposedExecutor;
Set the ThreadPoolExecutor's core pool size. Default is 1.
/** * Set the ThreadPoolExecutor's core pool size. * Default is 1. */
public void setCorePoolSize(int corePoolSize) { this.corePoolSize = corePoolSize; }
Set the ThreadPoolExecutor's maximum pool size. Default is Integer.MAX_VALUE.
/** * Set the ThreadPoolExecutor's maximum pool size. * Default is {@code Integer.MAX_VALUE}. */
public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; }
Set the ThreadPoolExecutor's keep-alive seconds. Default is 60.
/** * Set the ThreadPoolExecutor's keep-alive seconds. * Default is 60. */
public void setKeepAliveSeconds(int keepAliveSeconds) { this.keepAliveSeconds = keepAliveSeconds; }
Specify whether to allow core threads to time out. This enables dynamic growing and shrinking even in combination with a non-zero queue (since the max pool size will only grow once the queue is full).

Default is "false".

See Also:
  • allowCoreThreadTimeOut.allowCoreThreadTimeOut(boolean)
/** * Specify whether to allow core threads to time out. This enables dynamic * growing and shrinking even in combination with a non-zero queue (since * the max pool size will only grow once the queue is full). * <p>Default is "false". * @see java.util.concurrent.ThreadPoolExecutor#allowCoreThreadTimeOut(boolean) */
public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; }
Set the capacity for the ThreadPoolExecutor's BlockingQueue. Default is Integer.MAX_VALUE.

Any positive value will lead to a LinkedBlockingQueue instance; any other value will lead to a SynchronousQueue instance.

See Also:
/** * Set the capacity for the ThreadPoolExecutor's BlockingQueue. * Default is {@code Integer.MAX_VALUE}. * <p>Any positive value will lead to a LinkedBlockingQueue instance; * any other value will lead to a SynchronousQueue instance. * @see java.util.concurrent.LinkedBlockingQueue * @see java.util.concurrent.SynchronousQueue */
public void setQueueCapacity(int queueCapacity) { this.queueCapacity = queueCapacity; }
Specify whether this FactoryBean should expose an unconfigurable decorator for the created executor.

Default is "false", exposing the raw executor as bean reference. Switch this flag to "true" to strictly prevent clients from modifying the executor's configuration.

See Also:
  • unconfigurableExecutorService.unconfigurableExecutorService
/** * Specify whether this FactoryBean should expose an unconfigurable * decorator for the created executor. * <p>Default is "false", exposing the raw executor as bean reference. * Switch this flag to "true" to strictly prevent clients from * modifying the executor's configuration. * @see java.util.concurrent.Executors#unconfigurableExecutorService */
public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; } @Override protected ExecutorService initializeExecutor( ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { BlockingQueue<Runnable> queue = createQueue(this.queueCapacity); ThreadPoolExecutor executor = createExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, queue, threadFactory, rejectedExecutionHandler); if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } // Wrap executor with an unconfigurable decorator. this.exposedExecutor = (this.exposeUnconfigurableExecutor ? Executors.unconfigurableExecutorService(executor) : executor); return executor; }
Create a new instance of ThreadPoolExecutor or a subclass thereof.

The default implementation creates a standard ThreadPoolExecutor. Can be overridden to provide custom ThreadPoolExecutor subclasses.

Params:
  • corePoolSize – the specified core pool size
  • maxPoolSize – the specified maximum pool size
  • keepAliveSeconds – the specified keep-alive time in seconds
  • queue – the BlockingQueue to use
  • threadFactory – the ThreadFactory to use
  • rejectedExecutionHandler – the RejectedExecutionHandler to use
See Also:
Returns:a new ThreadPoolExecutor instance
/** * Create a new instance of {@link ThreadPoolExecutor} or a subclass thereof. * <p>The default implementation creates a standard {@link ThreadPoolExecutor}. * Can be overridden to provide custom {@link ThreadPoolExecutor} subclasses. * @param corePoolSize the specified core pool size * @param maxPoolSize the specified maximum pool size * @param keepAliveSeconds the specified keep-alive time in seconds * @param queue the BlockingQueue to use * @param threadFactory the ThreadFactory to use * @param rejectedExecutionHandler the RejectedExecutionHandler to use * @return a new ThreadPoolExecutor instance * @see #afterPropertiesSet() */
protected ThreadPoolExecutor createExecutor( int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue<Runnable> queue, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { return new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler); }
Create the BlockingQueue to use for the ThreadPoolExecutor.

A LinkedBlockingQueue instance will be created for a positive capacity value; a SynchronousQueue else.

Params:
  • queueCapacity – the specified queue capacity
See Also:
Returns:the BlockingQueue instance
/** * Create the BlockingQueue to use for the ThreadPoolExecutor. * <p>A LinkedBlockingQueue instance will be created for a positive * capacity value; a SynchronousQueue else. * @param queueCapacity the specified queue capacity * @return the BlockingQueue instance * @see java.util.concurrent.LinkedBlockingQueue * @see java.util.concurrent.SynchronousQueue */
protected BlockingQueue<Runnable> createQueue(int queueCapacity) { if (queueCapacity > 0) { return new LinkedBlockingQueue<>(queueCapacity); } else { return new SynchronousQueue<>(); } } @Override @Nullable public ExecutorService getObject() { return this.exposedExecutor; } @Override public Class<? extends ExecutorService> getObjectType() { return (this.exposedExecutor != null ? this.exposedExecutor.getClass() : ExecutorService.class); } @Override public boolean isSingleton() { return true; } }