ThreadPoolExector类源码理解

ThreadPoolExecutor类注释
一个ExecutorService通过线程池中的多个线程中的一个执行被提交的任务,通常采用Executors工程方法来配置。

线程池解决了两个不同的问题:
他们通常会在执行大量的异步任务时由于减少了每个任务的调用开销而改进了性能,他们提供了一个边界的定义并且管理资源,包括线程以及在执行任务集合时的消耗。每个ThreadPoolExeCutor都会维护着一些基本的统计信息,例如任务完成数。

在广泛的背景下有有用,这个类提供了许多可调参数和可扩展的钩子函数。然而,建议程序员使用更方便的Executors工厂方法,newCachedThreadPool(无界线程池,具有自动回收线程的功能),newFixedThreadPool(固定大小的线程池)和newSingleThreadExecutor(单个线程的线程池),它们预先为大多数常用的场景进行了配置。此外在手动配置和调优此类时,请使用以下指南:

  1. 核心和最大的线程池大小
    ThreadPoolExecutor将会自动调整线程池大小根据设置的corePoolSize和maximumPoolSize.
    当一个新的任务通过方法execute(Runnable)进行提交,并且线程池中运行的线程的数量是小于corePoolSize数时,一个新的线程将会被创建去处理这个请求,尽管其他的工作线程是闲置的。如果有超过CorePoolSize数量并且小于maximumPoolSize的线程正在运行,仅仅当等待队列是满的时候才会被创建。通过设置相同的corePoolSize和maximumPoolSize,你可以创建一个固定大小的线程池。通过设置maximumPoolSize本质上是无界的值例如Integer的最大值,你回允许线程池容纳任意数量的并发任务。通常core和maximum线程池大小是在构造器中设置,但是它们也可以通过setCorePoolSize和setMaximumPoolSize来动态的改变。
  2. 按需创建线程
    默认的情况,尽管核心线程都是在任务到达时创建和启动,但是这个方式能用方法prestartCoreThread和prestartAkkCoreThreads进行动态的覆写。如果要使用一个非空的队列,你可能要预先启动线程。
  3. 创建新的线程
    新线程是通过使用ThreadFactory类创建的。如果没有额外的要求,Executors.defaultThreadFactory用来创建线程并且这些线程都是在相同的ThreadGroup和具有相同的优先级以及不是守护线程。通过提供不同的ThreadFactory,你能够改变线程的名字,线程组,优先级,和守护进程状态等等。如果ThreadFactory在询问时没有创建线程并通过newThread返回null,这个executor将会继续执行,但是可能不会执行任何任务。线程应该拥有Runtime Permission类的modifyThread。如果使用线程池中的工作线程或者其他线程没有拥有这个权限,服务可能会被降级: 配置更改可能不会及时生效,shutdown池子可能会保持终止但尚未完成的状态。
  4. 存活时间
    如果线程池中当前有超过corePoolSize数量的线程,超过corePoolSize的线程如果闲置时间超过了keepAliveTime,他们就会被终止。它提供了一个当线程池没有被充分使用时减少资源消耗的方法。如果线程在之后又变得活跃起来,新的线程就会被创建。keep-alive这个参数能用setKeepAliveTime动态的改变。使用Long的最大值有效地禁止了空闲线程在shutdown之前被终止了。默认地这个keep-alive策略仅仅应用在线程数超过了corePoolSize。但是方法allowCoreThreadTimeOut能在核心线程应用超时策略,只要这个keepAliveTime不为0
  5. 队列
    任何BlockingQueue都可以被用作传输和保持提交的任务,队列的使用和线程池大小相互作用:
  • 如果正在运行的线程数小于corePoolSizr,这个Executor总是喜欢新建线程而不是队列
  • 如果有corePoolSize数量的线程或者多于这个数的线程正在运行,executor总是喜欢将请求入队而不是新建一个线程
  • 如果一个请求不能入队就会创建一个新的线程直到线程数超过了maximumPoolSize,在这种情况下,这个任务将会被丢弃

入队有三个通常的策略:

  • 直接切换:对于工作队列来说一个好的默认选择是SynchronousQueue,能够将任务直接交给线程而不用存储任务,如果没有可立即用于运行任务的线程,就会失败,因此一个新的线程将会被构建。这个策略避免出现当处理可能存在内部依赖的请求集合时的死锁。直接切换通常需要无界的线程池来避免出现拒绝提交的新任务,这反过来也承认了当命令以一个比处理的平均速度还快到达时,无界线程池中的线程可能会无限增长

  • 无界队列: 使用一个无界队列(例如LinkedBlockingQueue不预先指定初始化容量)会导致在所有的核心线程忙碌的时候,新的任务会在队列中一直等待,因此不会创建超过corePoolSize数量的线程(maximumPoolSize不会有任何的作用)。这个当每个任务完全独立于其他任务时,这个可能是合适的,所以任务不能相互影响执行;例如,在一个web页服务。而这种排队方式能用来消除请求峰值,它承认了当命令持续到达的平均速度比处理的速度快时,无界工作队列会快速增长。

  • 有界队列,一个有界队列(如ArrayBlockingQueue)可以帮助预防当使用有限的maximumPoolSize造成资源耗尽,但是这样可能更难调节和控制。队列大小和线程池最大大小可以相互交换,使用大队列和小池可以减少CPU使用量和操作系统资源已经上下文切换的开销,但会导致人为地降低吞吐量。如果任务频繁的阻塞,一个系统能够调度比你希望允许的还要多的线程。使用小队列通常要求大的线程池,这会使CPU更加繁忙,但是可能会遇到不能够接受的调度开销,并且会导致降低吞吐量

拒绝策略

用execute方法提交新的任务时,当Executor已经被shutdown或者Executor使用的有界线程池和工作队列已经饱和了新的任务会被拒绝。在任意情况下,execute方法将会调用ExecutionHandler的rejectExecution方法,有4个预先配置的handler策略被提供

  • 默认情况下的策略是ThreadPoolExecutor.AbortPolicy, handler在拒绝时会抛出一个运行时异常。
  • ThreadPoolExecutor.CallerRunsPolicy策略,这个线程调用它自己的execute方法运行这个任务。这提供了一个一个简单反馈控制机制,可以减慢新任务的提交速度。
  • ThreadPoolExecutor.DiscardPolicty策略,任务不能执行就被简单的抛弃。
  • ThreadPoolExecutor.DiscardOldestPolicy策略,如果这个executor没有被shutdown,在工作队列头部的任务被丢弃了,然后丢弃的任务会重试(重试之后,它可能会再次失败,导致这种情况再次发生)
    可以定义和使用其他类型的RejectedExecutionHandler类,这样做需要小心一些,尤其是策略被设计为仅在特定容量或队列策略下工作时。
    钩子方法
    这个类提供了可以重写的beforeExecute和afterExecute方法,这两个方法是在执行每个任务之前和之后调用的。这些能够被用来操作执行的环境。例如,重新初始化线程局部变量,收集统计信息或者添加日志项。另外可以重写方法terminated来执行执行器完全终止后需要执行的任何特殊处理。
    如果钩子和回调方法抛异常,内部工作线程反过来失败和突然终止。

队列维护
getQueue方法允许访问工作队列来进行监视和调试,强烈不鼓励将此方法用于任何其他的目的。当大量排队任务被取消时,remove和purge两个方法可以帮助回收存储内存。

最后
程序中不在引用并且没有剩余线程的线程池将自动shutdown。如果你希望确保回收未引用的线程池即使用户忘记调用shutdown,你必须设置合适的keep-alive时间,使用零核心线程的下限或者设置allowCoreTHreadTimeOut来安排未使用的线程最终消亡。

getTask注释:
根根当前的配置设置,对任务执行阻塞或定时等待,如果工作进程由于以下原因必须退出,则返回null:

  1. 有超过maximumPoolSize的工作进程(由于调用setMaxiMumPoolSize)
  2. 线程池状态是stop
  3. 线程池状态是shutdown并且队列是空的
  4. 工作线程在等待任务时超时,超时的工作线程将在定时等待之前和之后终止。{@code allowCoreThreadTimeOut | | workerCount>corePoolSize}。如果队列非空,则测工作线程不是池中的最后一个线程。

1
COUNT_BITS: 0000 0000 0000 0000 0000 0000 0001 1101
CAPACITY: 0001 1111 1111 1111 1111 1111 1111 1111
RUNNING: 0010 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN: 0000 0000 0000 0000 0000 0000 0000 0000

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值