Java线程池

利用Executors创建不同的线程池满足不同场景的需求

五种创建方式

截屏2021-01-27 下午12.15.08

为什么使用线程池

  1. 降低资源消耗
  2. 提高线程的可管理性

Executor框架

截屏2021-01-27 下午12.27.17

J.U.C(Java.util.concurrent)的三个Executor接口

  1. Executor:运行新任务的简单接口,将任务提交和任务执行细节解耦
  2. ExecutorService:具备管理执行器任务生命周期的方法,提交任务机制更完善
  3. ScheduledExecutorService:支持Future和定期执行任务

ThreadPoolExecutor

截屏2021-01-27 下午2.22.51

ThreadPoolExecutor的构造函数

参数:

  • corePoolSize:核心线程数量
  • MaximumPoolSize:线程不够用时能够创建的最大线程数
  • workQueue:任务等待队列
    • 常见队列类型:
      • SynchronouseQueue:直接交接,内部没有容量
      • LinkedBlokingQueue:无界队列,MaxPollSize参数等于没用了
      • ArraryBlokingQueue:有界队列
  • keepAliveTime:
    • 线程池维护线程所允许的空闲时间,当线程池的线程数大于corePoolSize时,如果此时没有新的线程提交,多余线程不会立即被销毁,而是直到等待时间超过keepAliveTime
  • threadFactory:
    • 作用:创建新线程,默认使用Executors.defaultThreadFactory()
    • 默认创建的线程拥有相同的优先级
  • handler:线程池的饱和策略
    • 如果阻塞队列满了并且没有空闲线程,此时如果继续提交任务,则需要采取一种策略处理该任务
    • 线程池提供了四种策略:
      • AbortPolicy:直接抛出异常,这是默认策略。
      • DiscardPolicy:直接丢弃当前任务
      • DiscardOldestPolicy:丢弃队列中最靠前(最老)的任务,并执行当前任务。
      • CallerRunsPolicy:用调用者所在的线程来执行任务。
        • 让主线程来执行也就没法再放任务进来,减轻负担
      • 可以通过实现RejectedExecutionHandler接口自定义handler

新任务提交execute执行后的判断:

截屏2021-03-06 下午6.23.30 截屏2021-01-27 下午6.43.18

线程池的状态:

  • RUNNING:能接受新提交的任务,也能处理阻塞队列的任务
  • SHUTDOWN:不再接受新的提交任务,但是可以处理存量任务
  • STOP:不再接受新的提交任务,也不处理存量任务
  • TIDYING:所有任务都已终止,此后讲运行terminated()方法
  • TERMINATED:terminated()方法执行完毕后进入该状态
截屏2021-01-27 下午6.50.30

工作线程的生命周期

截屏2021-01-27 下午7.25.41

线程池大小如何选定

  • CPU密集型:线程数 = 核数或核数+1
  • I/O密集型:线程数 = CPU核数*(1+平均等待时间/平均工作时间)

常见线程池

newFixedThreadPool

  • 队列是LinkedBlokingQueue,无上限,容易OOM内存溢出

newSingleThreadExecutor

单独的线程,线程池里只有一个线程,其他和newFixedThreadPool基本一致

newCachedThreadPool

可缓存线程池

特点:无界线程池,具有自动回收多余线程的功能

队列是SynchronousQueue:直接提交任务,不存储

CachedThreadPool

默认回收时间是60秒

newScheduledThreadPool

支持定时以及周期性执行任务的线程池

newWorkStealingPool

Fork/Join框架

Java7提供的用于并行执行任务的框架

  • 把大任务分割成若干小任务并行执行,最终汇总每个小任务结果后得到大任务结果的框架
  • 是ExecutorsService接口的具体实现
  • 使用working-stealing算法:某个线程从其他队列里窃取任务来执行
截屏2021-01-27 下午12.23.26