zoukankan      html  css  js  c++  java
  • Java线程池总结

    1. 关于ThreadPoolExecutor

    为了更好地控制多线程,JDK提供了一套Executor框架,帮助开发人员有效的进行线程控制,其本质就是一个线程池。其中ThreadPoolExecutor是线程池中最核心的一个类,后面提到的四种线程池都是基于ThreadPoolExecutor实现的。

    ThreadPoolExecutor提供了四个构造方法,我们看下最重要的一个构造函数:

    public class ThreadPoolExecutor extends AbstractExecutorService {
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler);
    }

    函数的参数含义如下:

    • corePoolSize: 线程池维护线程的最少数量
    • maximumPoolSize:线程池维护线程的最大数量
    • keepAliveTime: 线程池维护线程所允许的空闲时间
    • unit: 线程池维护线程所允许的空闲时间的单位
    • workQueue: 线程池所使用的缓冲队列
    • handler: 线程池对拒绝任务的处理策略

    线程池执行的过程:

      1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
      2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
        ​ a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
        ​ b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
        ​ c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
        ​ d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
      3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
      4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

    ThreadPoolExecutor中的队列:

    ThreadPoolExecutor内部应用了任务缓存队列,即workQueue,它用来存放等待执行的任务。

    workQueue的类型为BlockingQueue,通常可以取下面三种类型:

    1. ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
    2. LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
    3. synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

    任务拒绝策略:

    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

    ThreadPoolExecutor.AbortPolicy://丢弃任务并抛出RejectedExecutionException//异常。
    ThreadPoolExecutor.DiscardPolicy://也是丢弃任务,但是不抛出异常。
    ThreadPoolExecutor.DiscardOldestPolicy://丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy://由调用线程处理该任务

    常用函数

    shutdownNow()
    内部会立即试图调用 线程 Thread#interrupt(),来打断线程。
    若线程中有 Thread.sleep() 或 Object#wait() 阻塞了线程,会造成异常;所以线程内部运行的代码最好加上try-catch代码块。
    shutdown()
    等待线程内部任务执行完毕,再关闭线程池。
    即使线程中有 Thread.sleep() 或 Object#wait() 阻塞了线程,也不会造成异常;但最好对线程内部运行的代码加上try-catch代码块,因为可能是其它方面发生了异常。
    isShutdown() isTerminated()
    isShutdown() 在 线程池已关闭后,即调用shutdown()/shutdownNow(),会返回true。
    isTerminated() 在 线程池已关闭后,即调用shutdown()/shutdownNow(),会返回true 或者 false;取决于池内的所有任务是否都已关闭。
    getActiveCount()
    返回主动执行任务的近似线程数。
    因为在计算期间任务和线程的状态可能动态改变,所以返回值只是一个近似值。
    getTaskCount()
    返回曾计划执行的近似任务总数。
    getCompletedTaskCount()
    返回已完成执行的近似任务总数。
    awaitTermination(long timeout, TimeUnit unit)
    等待终止。给一个超时时间。超时时间前终止,返回true;终止前超时期满,则返回 false。需要与shutdown()/shutdownNow() 配合使用。

    2. 关于Executors提供的四种线程池

    Executors 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了 ExecutorService 接口。

    // 创建固定数目线程的线程池。
    public static ExecutorService newFixedThreadPool(int nThreads)
    
    // 创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。
    // 如果现有线程没有可用的,则创建一个新线 程并添加到池中。
    // 终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
    public static ExecutorService newCachedThreadPool()
    
    // 创建一个单线程化的Executor。
    public static ExecutorService newSingleThreadExecutor()
    
    // 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

    这四种方法都是用的 Executors 中的 ThreadFactory 建立的线程。

    newCachedThreadPool()

    • 缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse 如果没有,就建一个新的线程加入池中
    • 缓存型池子通常用于执行一些生存期很短的异步型任务 因此在一些面向连接的 daemon 型 SERVER 中用得不多。但对于生存期短的异步任务,它是 Executor 的首选。
    • 能 reuse 的线程,必须是 timeout IDLE 内的池中线程,缺省 timeout 是 60s,超过这个 IDLE 时长,线程实例将被终止及移出池。

    newFixedThreadPool(int)

    • newFixedThreadPool 与 cacheThreadPool 差不多,也是能 reuse 就用,但不能随时建新的线程。
    • 其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子。
    • 和 cacheThreadPool 不同,FixedThreadPool 没有 IDLE 机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的 TCP 或 UDP IDLE 机制之类的),所以 FixedThreadPool 多数针对一些很稳定很固定的正规并发线程,多用于服务器。
    • 从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同:
      • fixed 池线程数固定,并且是0秒IDLE(无IDLE)。
      • cache 池线程数支持 0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60 秒 IDLE 。

    newScheduledThreadPool(int)

    • 调度型线程池
    • 这个池子里的线程可以按 schedule 依次 delay 执行,或周期执行

    SingleThreadExecutor()

    • 单例线程,任意时间池中只能有一个线程
    • 用的是和 cache 池和 fixed 池相同的底层池,但线程数目是 1-1,0 秒 IDLE(无 IDLE)
  • 相关阅读:
    人类思考的基本形式
    晚上睡不者原因
    东西方哲学比较
    逻辑推理的三种方法
    锻炼自己的注意力和逻辑思维能力
    预测和复盘自己的投资策略
    概念:名与实
    没有“界定问题”会出现什么问题
    问题、联系-条条大路通罗马
    程序问题调试与医生、汽车维修师
  • 原文地址:https://www.cnblogs.com/deityjian/p/11087215.html
Copyright © 2011-2022 走看看