ThreadPoolExecutor
java.lang.Object
java.util.concurrent.AbstractExecutorService
java.util.concurrent.ThreadPoolExecutor
- 所 有已实现的接口:
- Executor, ExecutorService
强烈建议程序员使用较为方便的 Executors
工厂方法 Executors.newCachedThreadPool()
(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)
(固定大小线程池)和 Executors.newSingleThreadExecutor()
(单个后台线程),它们均为大多数使用场景预定义了设置。下面是对ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler)
的一些分析:
- 核心和最大池大小:ThreadPoolExecutor将根据corePoolSize和maximumPoolSize设置的边界自动调整大小。当新任务在方法
execute(java.lang.Runnable)
中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。 - 创建新线程:使用
ThreadFactory
创建新线程。如果没有另外说明,则在同一个ThreadGroup
中一律使用Executors.defaultThreadFactory()
创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回 null 时ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。 - 保持活动时间: 如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止。
-
排队:所有
BlockingQueue
都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:- 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
- 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
- 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
- 排队有三种通用策略:
- 直接提交。工作队列的默认选项是
SynchronousQueue
(一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有),它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。 - 无界队列:使用无界队列(例如,不具有预定义容量的
LinkedBlockingQueue
,一个基于已链接节点的、范围任意的 blocking queue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize,maximumPoolSize 的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列,例如,在 Web 页服务器中。 - 有界队列:当使用有限的 maximumPoolSizes 时,有界队列(如
ArrayBlockingQueue
,一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量;使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。 - 此类提供 protected 可重写的
beforeExecute(java.lang.Thread, java.lang.Runnable)
和afterExecute(java.lang.Runnable, java.lang.Throwable)
方法,这两种方法分别在执行每个任务之前和之后调用。如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。
posted on 2010-03-15 17:29 北冥有鱼,化而为鸟 阅读(1965) 评论(0) 编辑 收藏 举报