线程池

image-20220305111316950
  1. Executor:执行者,用来执行一个Runnable

    image-20220304203507319
  2. ExecutorService:拓展了Executor,完善了整个任务执行器的所有声明周期方法,这里遵循了接口隔离原则。

    image-20220304203539111
  3. AbstractExecutorService主要实现了submit、invokeAny、invokeAll方法,这里使用了模板设计模式,在这些方法中实现了模板代码逻辑,并暴露出一些抽象方法供子类实现

  4. ThreadPoolExecutor 则是具体的线程池的实现

  5. FoorkJoinPool,不同于ThreadPoolExecutor,它具有分解汇总任务,使用很少的线程可以执行很多的任务,非常适合CPU密集型的场景

ThreadPoolExecutor

image-20220305112306756

使用ThreadPoolExecutor

构造器

创建一个ThreadPool供需要提供如下几个参数:

image-20220305112525488
  1. corePoolSize 核心线程数,线程池默认的核心线程数,几十超过keepAlive也不会释放线程,他将会一直占用

  2. maximumPoolSize 最大线程数,最多可以创建的线程数

  3. keepAliveTime 如果一个线程持续这个时间,就将这个线程归还给操作系统,因为操作系统的线程数量有限,注意归还的是非核心线程

  4. unitkeepAliveTime的时间单位

  5. workQueue 指定任务队列,需要时一个阻塞队列,当来了一个任务时,没有空闲的线程可以处理,就将其放入到任务队列中

  6. threadFactory 用于产生线程的线程工厂,可以为线程指定名称,一些额外的日志操作

  7. handler 拒绝策略,如果任务队列也满了,就会进行拒绝策略(比如对未能处理的进行持久化)

拒绝策略

拒绝策略采用策略模式,其策略接口为RejectedExecutionHandler,共有以下几个实现类:

  1. AbortPolicy 直接抛出异常拒绝

  2. CallerRunsPolicy 调用run方法直接在主线程执行,这种方式也称为背压

  3. DiscardPolicy 内部什么都不做,新的线程将会被忽略 (不可使用)

  4. DiscardOldestPolicy 会poll掉workQueue的一个最老的任务,然后将当前的任务放入

线程池的状态

线程池的状态由ctl这个数字二进制的前三位存储(后面29位信息用来记录工作线程的数量),他的状态共分为五种,如下:

状态机的转换:

面试重点

  1. ctl 是什么

  2. 状态转换的过程

  3. 构造函数

  4. 拒绝策略

  5. runWorker方法的三个步骤

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 相对于 ThreadPoolExecutor 扩展了 ScheduledExecutorService 接口:

ForkJoinPool

Executors

线程池的工厂/工具类,他有以下几个常用的方法:

  1. SingleThreadExecutor

  2. CachedThreadPool,一般不会使用

  3. FixedThreadPool,固定长度的线程池

  4. ScheduledThreadPool,专为定时任务提供的线程池,使用的不多,应该使用定时任务框架(Quarts)

如何调整线程池的大小

  1. 如果线程数量设置的过多,那么他们会竞争稀缺的处理器和内存资源,造成大量的时间浪费在线程的切换上

  2. 如果线程数量设置的过少,那么处理器的一些核就无法被充分利用

  3. 线程池的短小可以使用下面的公式计算:

    image-20220305120916111
  4. 推荐使用压测

最后更新于

这有帮助吗?