参数列表

  1. corePoolSize 核心线程数
  2. maxPoolSize 最大线程数 在这里我把(maxPoolSize和maximumPoolSize 从作用上视为一种)
  3. keepAliveTime 保持存活时间
  4. workQueue 任务存储队列
  5. threadFactory 当线程池需要新的线程的时候,会使用threadFactory创建新的线程
  6. Handler 由于线程池无法接受你所提交的任务的拒绝策略 (任务拒绝处理器)

    参数说明及用法

  • 当线程数小于corePoolSize 时 即使其他线程处于空闲状态 也会创建新的线程处理新的任务
  • 当线程数等于(或者大于)corePoolSize 但小于 maxPoolSize 则将任务放入队列中
  • 当队列满了的时候 并且线程数小于maxPoolSize 则创建新线程来运行任务
  • 如果队列满了,并且线程数大于或等于maxPoolSize 则会拒绝任务
  • 当突破corePoolSize时 多余的线程空闲时间超过了keepAliveTime时 那他们就会被终止
  • ThreadFactory
    • 默认使用的是 Executors.defaultThreadFactory(),
    • 创建出来的线程都在同一个线程组
    • 拥有同样的优先级5
    • 不是守护线程
    • 如果自己指定ThreadFactory 就可以改变线程名,线程组,优先级,是否是守护线程
  • 常见的队列类型
    • 直接交换: SynchronousQueue
      • 用于任务不会特别多,只需要把任务通过队列进行简单的中转时
      • 因为该队列是没有容量的是存不下任务的
      • 如果使用该队列那maxPoolSize需要设置的大一点
    • 无界队列: LinkedBlockingQueue
      • 可以防治流量突增
      • 但也会有OOM异常的风险
    • 有界队列: ArrayBlockingQueue
      • 可以设置队列容量
      • 这时maxPoolSize就有意义了
  • 拒绝策略
    • AbortPolicy 直接抛出异常
    • DiscardPolicy 默默的丢弃任务
    • DiscardOldestPolicy 丢弃队列中最老的任务
    • CallerRunsPolicy 让提交任务的线程去执行新任务

      其他

      如果把corePoolSize和maxPoolSize设置成一样的,那就可以创建固定大小的线程池了 通过设置maxPoolSize很大的值,例如 Integer.MAX_VALUE 相当于创建了一个容纳任意并发任务的线程池 如果队列是无界队列(例如LinkedBlockingQueue)那么线程数就不会超过corePoolSize

线程池的创建形式

  • 手动创建 (推荐)
    • 更加明确线程池的运行规则
    • 避免资源耗尽的风险
  • 自动创建
    • newFixedThreadPool(int n) 固定数量的线程池

      可能会导致OOM 因为使用的是无界队列 当处理速度赶不上请求速度时 队列会越来越多

  1. new ThreadPoolExecutor(
  2. nThreads, // corePoolSize
  3. nThreads, // maxPoolSize
  4. 0L, TimeUnit.MILLISECONDS, // keepAliveTime
  5. new LinkedBlockingQueue<Runnable>() // 用的是无界队列
  6. );
  • newSingleThreadExecutor() 只有一个线程

    1. new ThreadPoolExecutor(
    2. 1, // corePoolSize
    3. 1, // maxPoolSize
    4. 0L, TimeUnit.MILLISECONDS, // keepAliveTime
    5. new LinkedBlockingQueue<Runnable>() // 用的是无界队列
    6. );
  • newCachedThreadPool() 可缓存的线程池

    当任务特别多时线程也会特别多 也会导致OOM

  1. new ThreadPoolExecutor(
  2. 0, // corePoolSize
  3. Integer.MAX_VALUE, // Integer 最大值
  4. 60L, TimeUnit.SECONDS, // keepAliveTime
  5. new SynchronousQueue<Runnable>() // 用的是直接交换队列
  6. );
  • newScheduledThreadPool(int corePoolSize)

    1. threadPool.schedule(Task,延期时间,时间单位); // 延迟时间之后执行
    2. threadPool.scheduleAtFixedRate(Task,延期时间,间隔时间,时间单位); // 延迟时间之后执行 然后每隔间隔时间循环执行
  • workStealingPool 1.8加入的

    • 递归子任务时 使用 (树的遍历,矩阵)
    • 窃取-其他线程可以处理其他子任务的队列数据

线程池数量设定

  • 计算密集型的:最佳设定为CPU数量的1-2倍
  • IO密集型的:一般设定大于CPU很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可以衔接上
    • Brain Goetz 推荐的计算方法 线程数 = CPU核心数*(1+平均等待时间/平均工作时间)

      停止线程池

  1. shutdown() 执行后是不能添加新任务了
  2. isShutdown() 是不是进入停止状态了
  3. isTerminated() 是不是真停止了
  4. awaitTerminated(时间,时间单位): boolean 测试一段时间后 线程池是否停止了
  5. shutdownNow() 立刻关闭掉

    1. 发送中断信号
    2. 返回未执行的任务列表

      钩子方法

  6. 每个任务执行前后调用

  7. 日志,统计
  8. beforeExecute(),afterExecute()

    类之间的关系

    image.png
  • Executors 是一个工具类

    线程池的状态

  • RUNNING:接收新任务并处理队列任务

  • SHUTDOWN: 不接收新任务,但处理队列任务
  • STOP: 不接收新任务,也不处理队列任务,并中断进行中的任务
  • TIDYING:所有任务都已终止并运行treminate()钩子方法
  • TERMINATED:terminate() 运行完成