zoukankan      html  css  js  c++  java
  • ThreadPoolExecutor的用法

    Java中的线程池

    • 一般我们说起Java中的线程池,其实指的是java.util.concurrent包下的ThreadPoolExecutor。当然java包下还有其他线程池的实现类,但主要也是最常用的就是这个类。今天我们来好好说说这个类。
    • 这里我们结合了其他人的整理和自己的思考进行了总结。

    1. 工作原理

    如图所示:


     
    ThreadPoolExecutor工作原理

    当主线程中调用execute接口提交执行任务时:
    则执行以下步骤:
    注意:线程池初始时,是空的。

    1. 如果当前线程数<corePoolSize,如果是则创建新的线程执行该任务
    2. 如果当前线程数>=corePoolSize,则将任务存入BlockingQueue<Runnable>
    3. 如果阻塞队列已满,且当前线程数<maximumPoolSize,则新建线程执行该任务。
    4. 如果阻塞队列已满,且当前线程数>=maximumPoolSize,则抛出异常RejectedExecutionException,告诉调用者无法再接受任务了。
    注意点:
    • 线程池初始化时,是空的。
    • 如果阻塞队列已满,且当前线程数<maximumPoolSize,则新建线程执行该任务。而不是新建线程,从阻塞队列里take任务来执行,所以这里并不是先来先执行的。
    提问:这里的阻塞队列是BlockingQueue<Runnale>,我们知道ThreadPoolExecutor是支持Callable任务提交的,那这里不会有问题吗?
    • 答:其实我们这里说的都是execute接口提交任务。execute接口只接受Runnable。其实我们看一下继承关系图:


       
      ThreadPoolExecutor类图
    • 可以看到其实ThreadPoolExecutor是继承了AbstractExecutorService,而AbstractExecutorService实现了ExecutorService。

    • 且我们可以看到ThreadPoolExecutor类里只重写了execute方法。ExecutorService的其他方法都没有实现,而是在AbstractExecutorService里实现的。所以说整个ThreadPoolExecutor里的策略都是只针对execute方法来说的。所以说上述的工作原理只针对execute接口。像其他的submit/invoke接口并不适用--因为调用这些接口其实调用的是AbstractExecutorService的实现。

    2. 我们来看一下ThreadPoolExecutor的参数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
    • corePoolSize:就是上图中的CorePoolSize,意为核心线程数。
    • maximumPoolSize:就是上图中maximumPoolSize,意为整个线程池的最大线程数。这个数字是包含orePoolSize的。
    • keepAliveTime和unit:是idle线程最大存活时间。unit是前者的单位。即如果当前线程数>corePoolSize,则会kill一些线程至corePoolSize大小。
    • workQueue:就是上面说的阻塞队列,注意BlockingQueue<Runnable>只是个接口,下面我们会细说一下它的常用实现类。
    • 下面两个参数是可选项:即Java重载了ThreadPoolExecutor的构造方法,下面两个参数可以不传,也可以只传一个。
    • threadFactory:线程工厂。创建线程的接口,该接口只有一个方法:Thread newThread(Runnable r);我们可以实现这个接口进而自定义创建线程,比如制定线程名称,线程组等。
    • handler:当线程池已满时,再提交任务会触发调用这个回调函数。RejectedExecutionHandler接口的唯一方法:
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    

    3. 阻塞队列的常用实现类

    • ArrayBlockingQueue: 有边界的阻塞队列。长度大小初始化时制定,即内部由数组实现。
    • LinkedBlockingQueue: 有边界的阻塞队列。边界是可选的,如果初始化的时候不指定则默认是Interger.MAX_VALUE,内部由链表实现。(最常用)
    • PriorityBlockQueue: 带有优先级的阻塞队列。没有边界。
    • 这三个类都实现了BlockingQueue接口,其中LinkedBlockingQueue最常用,特别注意一般一定要指定边界大小,不然线程池失去了些意义,且造成内存泄露。
      这里就不对这些细说,以后我们再细说。

    4. RejectedExecutionHandler线程池饱和策略

    • 默认的 ThreadPoolExecutor.AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException。
    • ThreadPoolExecutor.CallerRunsPolicy:调用线程直接call该任务的execute 本身。
    • ThreadPoolExecutor.DiscardPolicy:将任务删除。
    • ThreadPoolExecutor.DiscardOldestPolicy删除工作队列头部任务。

    5. ThreadPoolExecutor的扩展

    我们看ThreadPoolExecutor的源码发现如下三个函数的实现为空且是protected。明显用于子类实现的。

    protected void beforeExecute(Thread t, Runnable r) { }  
    protected void afterExecute(Runnable r, Throwable t) { }  
    protected void terminated() { } 
    
    • 在执行任务的线程中将调用beforeExecute和afterExecute等方法,在这些方法中还可以添加日志、计时、监视或者统计信息收集的功能。
    • 无论任务是从run中正常返回,还是抛出一个异常而返回,afterExecute都会被调用。如果任务在完成后带有一个Error,那么就不会调用afterExecute。
    • 如果beforeExecute抛出一个RuntimeException,那么任务将不被执行,并且afterExecute也不会被调用。
    • 在线程池完成关闭时调用terminated,也就是在所有任务都已经完成并且所有工作者线程也已经关闭后,terminated可以用来释放Executor在其生命周期里分配的各种资源,此外还可以执行发送通知、记录日志或者手机finalize统计等操作。
      即我们可以子类来继承ThreadPoolExecutor来定制化一些功能。


    链接:https://www.jianshu.com/p/f9c6bf9bb543

  • 相关阅读:
    AFNetWorking 文件上传 By-H罗
    利用系统APP实现导航---By张秀清
    项目 和 需求文档 -- 吴欧
    键盘弹起及lab时的动态计算高度 --董鑫
    NSSet和NSMutableSet
    内联函数 在ios中的运用 --黄仁斌
    iOS 七大手势之轻拍,长按,旋转手势识别器方法-赵小波
    网络技术之BGP
    [手游项目3]-3-golang
    [手游项目3]-2-git
  • 原文地址:https://www.cnblogs.com/xd502djj/p/13307743.html
Copyright © 2011-2022 走看看