Java线程池ThreadPoolExecutor

本文首发于我的微信公众号《andyqian》, 关注免费获取Java学习资料java

前言

  多线程一直是Java进阶的必修课。在Java中,咱们很早就知道能够经过 Thread 类和 Runnable 接口来实现多线程。与之有着相似职责的数据库链接,也可经过JDBC建立与使用。但咱们深知不管是数据库链接的建立与销毁,仍是线程的建立与销毁,都是一件及其消耗性能的事情。为了减小这种状况的发生,前辈们就在思考,是否是能够复用已有的数据库链接?减小建立,销毁动做?这就是后来数据库链接池的由来。一样的,为了复用线程,也就有了线程池。我一直独自暗喜,身为一位幸福的Java程序员,前有Java虚拟机管理内存,后有Doug Lea 大师提供并发库,锁机制。简直幸福的不像话,不过幸福归幸福,该掌握的仍是须要掌握的,咱们一块儿来看看今天的主角:ThreadPoolExecutor。程序员

简介

  在面试过程当中,也时常会遇到一些关于线程池的问题,例如:面试

  1. 线程池中核心参数有哪些?数据库

  2. 有没有本身实现过线程池?编程

  3. 若是让你本身实现线程池,你会怎么作?微信

多线程

这些问题,其实考核的就是Java线程池的知识,更具体一点就是对ThreadPoolExector类熟不熟悉,下面代码是ThreadPoolExector类的全参构造函数。下面咱们就一一对参数进行了解。并发

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

其中:ide

  1. corePoolSize:表示该线程池最小的工做线程数。默认状况下,当须要使用时建立线程,也能够调用 prestartAllCoreThreads() 方法进行预建立全部的核心线程。函数

  2. maximumPoolSize:表示该线程池最大的线程数量,理论上将其设置为无限大,就会建立无限多的线程,固然,建立线程的数量最终由系统资源也就是操做系统决定。

  3. keepAliveTime:表示空闲线程的超时时间,(单位为纳秒)。但在构造函数中,单位与unit 参数配合使用,最终转换为纳秒。

  4. unit:表示空闲线程超时的时间单位,可选值有:java.util.concurrent.TimeUnit中的值,SECONDS(秒),MINUTES(分),HOURS(小时),DAYS(天) 等。

  5. workQueue :表示工做队列(实际上是一个runnable队列,在线程池中定义为Worker),其基类为:java.util.concurrent.BlockingQueue。

  6. threadFactory:线程工厂,一般用于建立线程,以及命令规则。默认为: Executors.defaultThreadFactory()。

  7. handler 表示处理策略,当workQueue队列满时,以及建立线程错误时的处理策略。其基类为 java.util.concurrent.RejectedExecutionHandler。默认为:AbortPolicy 策略。

不一样组合

  在 java.util.concurrent.Executors类为咱们提供了多种组合,其底层仍是调用ThreadPoolExecutor。下面列举几个经常使用的方法:

1. newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }
...

特性:线程数量大小固定,且 corePoolSize 与 maximumPoolSize 数量相等。当线程数量设置太少时。task则会积压在LinkedBlockingQueue队列中。当 task 任务大于Integer.MAX_VALUE时 则会有OOM发生的风与之类是的还有newSingleThreadExecutor方法。

2. newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
 }

特性:corePoolSize数量为0,maximumPoolSize数量为Integer.MAX_VALUE,也就是说理论上是能够建立Integer.MAX_VALUE个线程的。keepAiveTime时间为 60秒。BlockingQueue使用的是SynchronousQueue,因为其没用容量,意味这每一次put对应着一次take操做,其吞吐量比较高。正由于如此,当task到达必定程度时,可能会建立许多线程,从而致使OOM,甚至服务不可用。

 

上述方法实际上是对ThreadPoolExecutor方法的封装对不对,知道了ThreadPoolExecutor的每个参数,再来使用这个,就驾轻就熟了对不对。

规范

Executors类这么方便,是否是能够直接使用Executors类来建立呢?固然能够,但并不建议这样作。在《阿里Java手册》中的并发处理小节中有提到:

【强制】线程池不容许使用 Executors 去建立,而是经过 ThreadPoolExecutor 的方式,这样的处理方式可让写的同窗更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors 返回的线程池对象的弊端以下:
1) FixedThreadPool 和 SingleThreadPool :
容许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而致使 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :容许的建立线程数量为 Integer.MAX_VALUE ,可能会建立大量的线程,从而致使 OOM 。

在实际应用中,咱们应该遵照规范,避免掉一些不必的问题。该规约其最终目的是让你们可以更清楚了解线程池的每一个参数,从而达到可以在实际应用场景中调为最优组合使用,使其达到最大性能。一样的,咱们也经过安装阿里巴巴的规约插件进行自动扫描与提醒。在Idea中 File -> Setings -> Plugins -> Browse repositories中搜索『Alibaba Java Coding Guidelines』安装便可!

小结

在这篇文章中,算是对ThreadPoolExector的一个初步了解。知道了其核心参数,究竟是怎么回事。但这还并不够,且不足以学以至用,还有不少疑问,如:

  1. ThreadPoolExecutor的原理是怎样的?

  2. 咱们如何自定义一个线程池?

  3. ThreadPoolExecutor在Dubbo中的实践

这些疑惑都须要一一去揭晓。因为篇幅缘由,这些会做为好几篇文章进行记录。其目的是可以学以至用,面试时也能驾轻就熟。


 

相关阅读:

使用 Mybatis 真心不要偷懒!

再谈Java 生产神器 BTrace

Java 生产神器  BTrace

重构不彻底指南!


 

若是想深刻学习Java并发编程,《Java并发编程的艺术》这本书是值得阅读的。固然了,若是你读喜欢电子书,也能够回复公众号消息『Java并发编程的艺术』进行免费获取!

 

关注免费获取Java学习资料

相关文章
相关标签/搜索