ThreadPoolExecutor 线程池使用
开发规范
阿里巴巴开发规范中指出了3点和线程使用相关的强制措施。
1、创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
2、线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
3、线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
不允许使用 Executors 创建的原因如下。
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
手动创建线程池的方法
ThreadPoolExecutor
提供了几个构造方法,拿参数最全的举例说明。
参数意义
corePoolSize
,核心线程数,即便线程空闲也会存活的数量maximumPoolSize
, 最大线程数,允许创建线程的最大数量keepAliveTime
,超过核心线程数时,空闲线程等待 keepAliveTime 时间后才进行回收unit
,keepAliveTime 的时间单位workQueue
, 任务执行前所保存到的队列,可自定义长度,FixedThreadPool
和SingleThreadPool
使用的是LinkedBlockingQueue
,默认最大长度是 Integer.MAX_VALUEthreadFactory
,创建线程的工厂,可以使用 guava 的new ThreadFactoryBuilder().setNameFormat("我的线程-task-%d").build()
来创建一个自定义名称的线程工厂handler
,拒绝策略的实现,当线程池异常关闭或者任务队列满时,再次提交任务则会执行拒绝策略AbortPolicy
,默认的策略,抛出异常DiscardPolicy
,直接丢弃DiscardOldestPolicy
,丢弃队列中最老的任务,然后尝试执行()CallerRunsPolicy
,由调用线程执行此任务
其它构造方法
最少参数的构造方法使用默认线程工厂和默认拒绝策略。
线程的创建过程
使用线程池的 execute(Runnable command)
方法提交任务。
如果当前线程数小于
corePoolSize
,则创建新的线程如果当前线程数大于等于
corePoolSize
,则加入到工作队列workQueue
中,当队列已满,则创建新的线程线程数达到
maximumPoolSize
,且队列满时,将执行拒绝策略
思考
在有大量任务执行的场景,如果使用不恰当的创建线程池的方式,可能会引起线上的问题,例如 OOM。所以阿里强制程序员使用自定义线程池,创建线程池时清楚地指定好各个参数,避免深度的封装类导致不知道实现细节而踩坑。
版权声明: 本文为 InfoQ 作者【郭儿的跋涉】的原创文章。
原文链接:【http://xie.infoq.cn/article/b18f508b38273b94e7e72c87c】。文章转载请联系作者。
评论