jdk5.0 多线程学习笔记(六)

从前面的文章可以看出,jdk1.5为我们提供了很多线程池

这里做一下简要的说明:

类Executors,提供了一些创建线程池的方法

 

newFixedThreadPool(int nThreads)

创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。

如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。

 

newCachedThreadPool()

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。

调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。

终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

注意,可以使用 ThreadPoolExecutor类 构造方法创建具有类似属性但细节不同,(例如超时参数)的线程池。

其实executors的线程池也是基于ThreadPoolExecutor扩展的。

 

newSingleThreadExecutor()

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,

一个新线程将代替它执行后续的任务)。

可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

(与其他等效的 newFixedThreadPool(1) 不同,

可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。)

 

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
 核心和最大池大小 corePoolSize - 池中所保存的线程数,包括空闲线程。 maximumPoolSize - 池中允许的最大线程数。

ThreadPoolExecutor 将根据 corePoolSize和 maximumPoolSize设置的边界自动调整池大小。

当新任务在方法 execute(java.lang.Runnable)中提交时,

如果运行的线程少于corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。

如果运行的线程多于corePoolSize 而少于maximumPoolSize,则仅当队列满时才创建新线程。

如果设置的corePoolSize 和maximumPoolSize 相同,则创建了固定大小的线程池。

如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),

则允许池适应任意数量的并发任务。

在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int)

setMaximumPoolSize(int)进行动态更改。

默认情况下,即使核心线程也只是在新任务需要时才创建和启动的。

 

threadFactory - 执行程序创建新线程时使用的工厂 

使用 ThreadFactory创建新线程。

如果没有另外说明,则在同一个ThreadGroup中一律使用Executors.defaultThreadFactory()创建线程,

并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。

通过提供不同的ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。

如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。

 

keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。 

(保持活动时间)

unit - keepAliveTime 参数的时间单位。

如果池中当前有多于corePoolSize的线程,则这些多出的线程在空闲时间超过keepAliveTime时将会终止。

这提供了当池处于非活动状态时减少资源消耗的方法。

如果池后来变得更为活动,则可以创建新的线程。

也可以使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit)动态地更改此参数。

使用 Long.MAX_VALUE TimeUnit.NANOSECONDS的值在关闭前有效地从以前的终止状态禁用空闲线程。

 

workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。 

 

所有BlockingQueue都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

1.如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

2.如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

3.如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,

在这种情况下,任务将被拒绝。

 

排队有三种通用策略:

1.直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。

在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。

此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。

直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。

当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

 

2.无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue

将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。

这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)

当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。

这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

 

3.有界队列。当使用有限的 maximumPoolSizes 时,

有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。

队列大小和最大池大小可能需要相互折衷:

使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,

但是可能导致人工降低吞吐量。

如果任务频繁阻塞(例如,如果它们是 I/O 边界),

则系统可能为超过您许可的更多线程安排时间。

使用小型队列通常要求较大的池大小,CPU 使用率较高,

但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

 

handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序 (被拒绝的任务 )

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,

在方法 execute(java.lang.Runnable)中提交的新任务将被拒绝

在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler

RejectedExecutionHandler.rejectedExecution(java.lang.Runnable,
java.util.concurrent.ThreadPoolExecutor)

 

 方法。

下面提供了四种预定义的处理程序策略:

 1.在默认的 ThreadPoolExecutor.AbortPolicy中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException

2.在 ThreadPoolExecutor.CallerRunsPolicy中,线程调用运行该任务的 execute 本身。

此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

3. 在 ThreadPoolExecutor.DiscardPolicy中,不能执行的任务将被删除。

4.在 ThreadPoolExecutor.DiscardOldestPolicy中,如果执行程序尚未关闭,

则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

 

定义和使用其他种类的 RejectedExecutionHandler类也是可能的,但这样做需要非常小心,

尤其是当策略仅用于特定容量或排队策略时。

这些只是jdk中的介绍,要想更好的使用线程池,就需要对这些参数有所了解。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值