写在前面 在Java应用开发中经常会用到连接池、线程池等池化技术。池化(pool)技术的本质是通过复用对象、连接等资源,减少创建对象/连接,降低垃圾回收(GC)的开销,适当使用池化相关技术能够显著提高系统效率,优化性能。 线程池
Java ThreadPoolExecutor 线程池通过减少频繁创建和销毁线程来降低系统性能损耗。每一个线程会对应一个线程栈(Thread Stack),用于存储局部变量与操作信息,可通过JVM参数中 -Xss 来调整线程栈大小,同时通过线程池来控制可创建线程的数量。线程池一般和队列(work queue)配合工作,通过线程池限制处理并发任务的数量;然后使用队列做为缓冲,通过设置队列的深度,当任务数量超过队列深度时,采用相应的拒绝策略处理,保证系统的可用性。 ThreadPoolExecutor构造方法 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);参数解析 corePoolSize:核心线程数量,是线程池维护的线程最小数量。它会一直存活,即使没有任务,线程池也会维护线程的最少数量。
maximumPoolSize:线程池维护线程的最大数量。
keepAliveTime: 线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
unit:线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue:线程池所使用的缓冲队列,包括有界堵塞队列ArrayBlockingQueue、链表阻塞队列LinkedBlockingQueue、无缓冲阻塞队列SynchronousQueue。
handler:线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,包括Abort(直接抛出RejectExecutionException),Discard(按照LIFO丢弃)、DiscardOldest(按照LRU丢弃)、CallsRun(主线程执行),默认值ThreadPoolExecutor.AbortPolicy()。
策略说明 当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果当前线程池中的数量小于corePoolSize,并线程池处于Running状态,创建并添加的任务。 如果当前线程池中的数量等于corePoolSize,并线程池处于Running状态,缓冲队列 workQueue未满,那么任务被放入缓冲队列、等待任务调度执行。 如果当前线程池中的数量大于corePoolSize,缓冲队列workQueue已满,并且线程池中的数量小于maximumPoolSize,新提交任务会创建新线程执行任务。 如果当前线程池中的数量大于corePoolSize,缓冲队列workQueue已满,并且线程池中的数量等于maximumPoolSize,新提交任务由Handler处理。 当线程池中的线程大于corePoolSize时,多余线程空闲时间超过keepAliveTime时,会关闭这部分线程。 建议显式设置队列深度 使用Executor.newFixedThreadPool时,没有显式设置work queue队列大小(默认为Integer.MAX_VALUE),队列深度过大导致瞬间线程数量巨大;
如果大量任务被缓存到LinkedBlockingQueue中等待线程执行,会导致垃圾回收(GC)消耗大,系统响应慢以及内存溢出(OOM);
tomcat的线程池相关配置支持Http Connector与Executor两种,下面分别说明。
Http Connector 参考:http://tomcat.apache.org/tomcat-8.5-doc/config/http.html
Tomcat-Http-Connector <Connector port="8080" acceptCount="100" maxConnections="200" minSpareThreads="10" maxThreads="200" />主要参数解析 acceptCount:请求等待队列大小。当Tomcat没有空闲线程处理连接请求时,新请求将被放入等待队列;当队列深度超过acceptCount后,新请求直接被拒绝,默认值为100。
maxConnections:能够处理的最大并发连接数。当超过后会将新的连接放入等待队列,连接会等待并不会被处理。默认值根据Connector的类型会有区别,BIO为200,NIO、NIO2为10000,APR为8192。
minSpareThreads:线程池最小连接数,即可维护的最小连接数,默认值为10。
maxThreads:线程池最大连接数,当线程空闲一段时间后会释放,直到minSparethreads数量,默认值为200。
配置建议 Tomcat最大并发线程数石油maxConnections与maxThreads中较小的值决定。
例如:
并发请求200个,accessCount=100,maxConnections=50,maxThreads=100;
则说明有50个线程处理50个并发连接,100个线程进入等待队列,其余50个请求被拒绝。 Executor(org.apache.catalina.Executor)方式配置 参考:http://tomcat.apache.org/tomcat-8.5-doc/config/executor.html
Tomcat-Executor <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" daemon="true" minSpareThreads="25" maxThreads="200" maxIdleTime="60000" maxQueueSize="Integer.MAX_VALUE" prestartminSpareThreads="false" /> <Connector port="8080" executor="tomcatThreadPool" executorTerminationTimeoutMills="5000"/>主要参数解析 namePrefix:线程池创建的线程名称前缀。
daemon:是否以守护进程方式运行,默认为true。
minSpareThreads:线程池最小线程数,默认值为25。
maxThreads:线程池最大连接数,默认值为200。
maxIdleTime:空闲线程的存活时间,超过此时间线程将会被回收,默认值为60秒。
maxQueueSize:任务队列深度,默认为Integer.MAX_VALUE。
prestartminSpareThreads:是否在Tomcat启动时就在线程池中创建minSpareThreads个线程,默认值为false。
executorTerminationTimeoutMills:在停止Executor时,等待请求处理线程终止的超时时间,默认值为5000毫秒(5秒)。
配置建议 maxQueueSize默认值为Integer.MAX_VALUE,在常规场景过大,建议通过压测合理调整此值;
关注公众号一起探讨技术:码农翻身之道