本文源码的jar包是org\springframework\spring-context\3.2.14.RELEASE
1. 为什么要用线程池?
合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务
到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会
降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。(参见
ImportnNew(http://www.importnew.com/17633.html))
然后我看到自己项目里面写的代码,就更能明白线程池的好处了:
代码注释掉的地方就是又起了一个线程去执行任务,实际上可以直接用线程池的,没有必要再去消耗资源去起一个线程,看到taskExecutor.execute(......),我就去了解了一下线
程池,看了一下它的源码,追本溯源。另外,用线程池的目的也是让主线程和子线程异步的去执行,互不影响,一边我去解析和落地我的数据,一边我去做返回。
package com.pingan.toa.asset.facade.creditcard.datarec.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import com.pingan.toa.asset.common.constant.CreditCardConstants;
import com.pingan.toa.asset.common.enums.ResponseTypeEnums;
import com.pingan.toa.asset.facade.creditcard.datarec.CustomerBasicInfoFacade;
import com.pingan.toa.asset.model.response.CommonResponse;
import com.pingan.toa.asset.service.creditcard.datarec.DataReceiveAndParseService;
@Component("customerInfoFacadeImpl")
public class CustomerBasicInfoFacadeImpl implements CustomerBasicInfoFacade {
@Autowired
private DataReceiveAndParseService dataReceiveAndParseService;
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
private static final Logger logger = LoggerFactory.getLogger(CustomerBasicInfoFacadeImpl.class);
@Override
public CommonResponse<String> postCustomerBasicInfo(final String allData) {
logger.info("CustomerBasicInfoFacadeImpl postCustomerBasicInfo start....,allData:{}",allData);
try{
taskExecutor.execute(new Runnable() {
@Override
public void run() {
logger.info("开始调用解析落地数据线程..........");
dataReceiveAndParseService.parseDataAndSave(allData);
logger.info("结束调用解析落地数据线程..........");
}
});
}catch(Exception e){
logger.error("task run error,{}",e);
}
return new CommonResponse<String>(ResponseTypeEnums.SUCCESS, null,null, CreditCardConstants.DATA_SAVE_SUCCESS_MSG);
}
/*public class Task extends Thread{
private String recData;
public Task(String recData) {
this.recData = recData;
}
@Override
public void run() {
logger.info("开始调用解析落地数据线程..........");
dataReceiveAndParseService.parseDataAndSave(recData);
try {
sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("结束调用解析落地数据线程..........");
}
}*/
}
2. taskExecutor.execute(...) 具体背后的原理是什么?
ctrl+F3看execute方法如下:
public void execute(Runnable task) {
Executor executor = getThreadPoolExecutor();
try {
executor.execute(task);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}
那getThreadPoolExecutor()又是如何得到的呢?
/**
* Return the underlying ThreadPoolExecutor for native access.
* @return the underlying ThreadPoolExecutor (never {@code null})
* @throws IllegalStateException if the ThreadPoolTaskExecutor hasn't been initialized yet
*/
public ThreadPoolExecutor getThreadPoolExecutor() throws IllegalStateException {
Assert.state(this.threadPoolExecutor != null, "ThreadPoolTaskExecutor not initialized");
return this.threadPoolExecutor;
}
所以说ThreadPoolTaskExecutor会先进行一个初始化,然后调用
getThreadPoolExecutor()就可以得到一个Executor。那么我们来看一下它的初始化方法:
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
首先对初始化方法里的new ThreadPoolExecutor(......)方法里的几个参数做一下说明:
/**
* Creates a new <tt>ThreadPoolExecutor</tt> with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the
* pool, even if they are idle.
* @param maximumPoolSize the maximum number of threads to allow in the
* pool.
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the keepAliveTime
* argument.
* @param workQueue the queue to use for holding tasks before they
* are executed. This queue will hold only the <tt>Runnable</tt>
* tasks submitted by the <tt>execute</tt> method.
* @param threadFactory the factory to use when the executor
* creates a new thread.
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached.
* @throws IllegalArgumentException if corePoolSize or
* keepAliveTime less than zero, or if maximumPoolSize less than or
* equal to zero, or if corePoolSize greater than maximumPoolSize.
* @throws NullPointerException if <tt>workQueue</tt>
* or <tt>threadFactory</tt> or <tt>handler</tt> are null.
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
corePoolSize:线程池的基本大小,
当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会
创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。(参见ImportnNew(http://www.importnew.com/17633.html))
maximumPoolSize:线程池里最大线程数
keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大
这个时间,提高线程的利用率。(参见ImportnNew(http://www.importnew.com/17633.html))
unit是keepAliveTime的单位
workQueue:是任务队列,当任务大于线程数时,要有个阻塞队列来保存这些等待执行的任务。BlockingQueue它有几种实现方式,分别是:
LinkedBlockingQueue,SynchronousQueue,ArrayBlockingQueue,PriorityBlockingQueue等,它们之间的区别已经该用何种队列可以去看一
下它们的源码,以及它里面的方法(参见http://blog.csdn.net/xin_jmail/article/details/26157971)。但可以看到源码里面创建队列的方法
createQueue(......)它里面却只用了两种队列LinkedBlockingQueue,SynchronousQueue,根据队列的容量来决定
/**
* 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<Runnable>(queueCapacity);
}
else {
return new SynchronousQueue<Runnable>();
}
}
ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常有帮助。(参见
ImportnNew(http://www.importnew.com/17633.html))
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略
默认情况下是AbortPolicy,表示无法处理新任务时抛出异常(参见ImportnNew(http://www.importnew.com/17633.html))
3. 那么像queueCapacity等这些参数是怎么配置的呢?
如果没有在xml文件里面配置的话,ThreadPoolTaskExecutor是有默认值的,如下:
private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private boolean allowCoreThreadTimeOut = false;
private int queueCapacity = Integer.MAX_VALUE;
可以看到
像
queueCapacity默认值是最大值,这显然是不合理的。那么我们需要在配置文件里进行设置:
http://www.springframework.org/schema/task/spring-task.xsd
<task:executor id="taskExecutor" pool-size="5-500" queue-capacity="10000"/>
先引入schema,再引入标签
那么spring究竟是怎么读到pool-size和queue-capacity这个值的呢,并且queue-capacity是怎么赋给queueCapacity的呢?
我们可以看一下源码,Parser for the 'executor' element of the 'task' namespace,就一目了然了
/**
* Parser for the 'executor' element of the 'task' namespace.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 3.0
*/
public class ExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.scheduling.config.TaskExecutorFactoryBean";
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String keepAliveSeconds = element.getAttribute("keep-alive");
if (StringUtils.hasText(keepAliveSeconds)) {
builder.addPropertyValue("keepAliveSeconds", keepAliveSeconds);
}
String queueCapacity = element.getAttribute("queue-capacity");
if (StringUtils.hasText(queueCapacity)) {
builder.addPropertyValue("queueCapacity", queueCapacity);
}
configureRejectionPolicy(element, builder);
String poolSize = element.getAttribute("pool-size");
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
}
private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {
String rejectionPolicy = element.getAttribute("rejection-policy");
if (!StringUtils.hasText(rejectionPolicy)) {
return;
}
String prefix = "java.util.concurrent.ThreadPoolExecutor.";
if (builder.getRawBeanDefinition().getBeanClassName().contains("backport")) {
prefix = "edu.emory.mathcs.backport." + prefix;
}
String policyClassName;
if (rejectionPolicy.equals("ABORT")) {
policyClassName = prefix + "AbortPolicy";
}
else if (rejectionPolicy.equals("CALLER_RUNS")) {
policyClassName = prefix + "CallerRunsPolicy";
}
else if (rejectionPolicy.equals("DISCARD")) {
policyClassName = prefix + "DiscardPolicy";
}
else if (rejectionPolicy.equals("DISCARD_OLDEST")) {
policyClassName = prefix + "DiscardOldestPolicy";
}
else {
policyClassName = rejectionPolicy;
}
builder.addPropertyValue("rejectedExecutionHandler", new RootBeanDefinition(policyClassName));
}
}