一 构造方法
ThreadPoolExecutor的完整构造方法在JDK中的说明如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
-
用给定的初始参数创建新的 ThreadPoolExecutor。
-
参数:
-
corePoolSize
- 池中所保存的线程数,包括空闲线程。 -
maximumPoolSize
- 池中允许的最大线程数。 -
keepAliveTime
- 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。 -
unit
- keepAliveTime 参数的时间单位。 -
workQueue
- 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。 -
threadFactory
- 执行程序创建新线程时使用的工厂。 -
handler
- 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
-
二 ThreadPoolExecutor的处理原则
2.1 运行原理
ThreadPoolExecutor处理过程中,主要使用到的容器就是 池 和 排队队列,通过池 和 队列实现线程的调度工作。
——池:使用HashSet来存放线程
——排队队列:BlockingQueue
通过查看ThreadPoolExecutor的execute方法源码,总结出该方法遵循如下处理逻辑:
1 尝试将提交的线程加入线程池,成功,则退出,否则,进入步骤2
2 尝试将线程加入排队队列,成功,则退出,否则,进入步骤3
3 重新尝试将线程加入线程池,成功,则退出,否则,进入步骤4
4 进行reject处理(参见reject策略)
说明:
【1】 尝试加入线程池失败包括"线程池当前线程数大于等于corePoolSize" 和"加入池操作失败"两种情况。(加入池HashSet是同步操作,加入前会判断当前池的容量是否仍小于corePoolSize,并判断当前池的状态是否是RUNNING)
【2】 尝试加入队列时,会校验线程池当前的状态是否为RUNNING
【3】 加入排队队列成功后,execute方法会进行保障工作:a) 当前线程池处在非RUNNING状态,则将本轮执行的线程(注意仅本次execute的线程)从排队队列中清除;b) 当前线程池在RUNNING状态,并且排队队列不为空时,查看当前线程池是否为空,如果为空,则创建一个空闲线程
【4】 再次尝试将西安城加入线程池时,execute会判断线程池的状态和当前池的大小是否小于maximumPoolSize
ThreadPoolExecutor的execute方法处理流程图
2.2 排队策略理解
在学习ThreadPoolExecutor过程中,排队策略对线程的执行顺序的影响是比较难理解的。在JDK说明文档中,列举了3中排队策略,分别对应着不同的BlockingQueue实现。
- 直接提交。工作队列的默认选项是
SynchronousQueue
,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。 - 无界队列。使用无界队列(例如,不具有预定义容量的
LinkedBlockingQueue
)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。 - 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如
ArrayBlockingQueue
)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
-
将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回true,如果当前没有可用的空间,则返回false。当使用有容量限制的队列时,此方法通常要优于
add(E)
,后者可能无法插入元素,而只是抛出一个异常。 -