线程池简析

线程的本质

线程池简析


Java 线程池

  • newCachedThreadPool
    • 创建一个可缓存的无界线程池,该方法无参数。
    • 只有非核心线程,线程数量不定的线程池,为无限大,最大线程数为Integer.MAX_VALUE
    • keepAliveTime为60秒
    • 使用SynchronousQueue作为工作队列。这种队列不存储元素,任务提交后必须有空闲线程立即接收,否则会创建新线程(如果未达到maximumPoolSize)。
    • 如果任务提交速度过快,会创建大量线程(理论上可达Integer.MAX_VALUE个),可能耗尽系统资源,导致OOM以及频繁的上下文切换。
  • newFixedThreadPool
    • 创建一个定长线程池
    • 使用无界的LinkedBlockingQueue作为工作队列
    • 无超时机制
    • 当任务提交速度远大于处理速度时,队列会持续增长,可能导致内存溢出(OOM)
  • newSingleThreadExecutor
    • 创建只有一个线程的线程池,该方法无参数
    • 所有任务都保存无界的LinkedBlockingQueue中,等待唯一的单线程来执行任务,并保证所有任务按照指定顺序(FIFO或优先级)执行。
    • 无界队列可能导致OOM
  • newScheduledThreadPool/newSingleThreadScheduledExecutor
    • 创建一个定长线程池,支持定时及周期性任务执行。ScheduledExecutorService比Timer更安全,功能更强大
    • 可指定线程池的核心线程个数
    • 非核心数量无限制,maximumPoolSize为Integer.MAX_VALUE。但由于队列是DelayedWorkQueue,通常不会无限增长线程,除非有大量同时到期的任务且处理不过来
    • 非核心线程闲置时立即被回收
    • 任务队列是DelayedWorkQueue (一种特殊的PriorityQueue)

线程池参数

corePoolSize:

  • 如果当前运行的线程数小于corePoolSize,则创建新线程(即使有空闲的核心线程)来执行任务。
  • 除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。

maximumPoolSize:

  • 如果workQueue.offer()失败(队列已满),创建小于maximumPoolSize的线程
  • 如果是有界队列,当队列满时,仍然有任务进来,此时线程池会创建小于最大线程数的线程。
  • 如果是无界队列,会一直保持核心线程数,多余的任务会一直往队列中加入。

keepAliveTime:

  • 非核心线程空闲存活时间
  • allowCoreThreadTimeOut(boolean) 方法也可将此超时策略应用于核心线程
  • 也可以使用setKeepAliveTime()动态地更改参数

workQueue:

  • 阻塞队列,核心线程数满时,新加入的任务,会先添加到阻塞队列中等待线程获取任务并执行。
  • BlockingQueue的插入/移除/检查这些方法,对于不能立即满足但可能在将来某一时刻可以满足的操作,共有4种不同的处理方式
    • 第一种是抛出一个异常
    • 第二种是返回一个特殊值(null 或 false,具体取决于操作)
    • 第三种是在操作可以成功前,无限期地阻塞当前线程
    • 第四种是在放弃前只在给定的最大时间限制内阻塞。

threadFactory:

  • 创建新线程的工厂
  • 可以为线程池中的线程设置更有意义的名称、设置守护线程状态、设置线程优先级、指定UncaughtExceptionHandler等
  • Executors.defaultThreadFactory()是默认实现

handler:

  • 核心线程和非核心线程都已用尽,且队列也满了,执行RejectedExecutionHandler所定义的拒绝策略
  • 线程饱和策略,默认策略为AbortPolicy,直接在当前线程抛出异常。
  • 实现RejectedExecutionHandler接口自定义拒绝策略,可以做一些打印观察日志的操作,告警、兜底的相关处理等。
  • ThreadPoolExecutor.AbortPolicy:处理程序遭到拒绝,则直接抛出运行时异常 RejectedExecutionException。(默认策略)
  • ThreadPoolExecutor.CallerRunsPolicy:提交任务的线程,直接执行任务。此策略提供简单的反馈控制机制,变相的背压机制,能够减缓新任务的提交速度。
  • ThreadPoolExecutor.DiscardPolicy:直接丢弃被拒绝的任务,不做任何通知
  • ThreadPoolExecutor.DiscardOldestPolicy:位于工作队列头部的任务(最旧的任务)将被删除,然后重新尝试执行任务(如果再次失败,则重复此过程)。

线程池类

  • Executor
    • 顶层接口
    • 将任务提交和任务执行进行解耦
    • 用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。
  • ExecutorService
    • 继承Executor的接口
    • 提供了更完善的生命周期管理能力,通过Future对象提供任务取消、状态查询、结果获取能力实现了任务监控。
  • AbstractExecutorService
    • 抽象类
    • 将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可
  • ThreadPoolExecutor
    • 核心实现
    • 提供了高度可配置的线程池,允许我们精细控制线程池的各种行为
    • 一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。
  • ScheduledThreadPoolExecutor
    • ScheduledExecutorService接口的实现类,继承ThreadPoolExecutor
    • 专门用于处理定时和周期任务
  • Executors
    • 一个静态工厂模式的工具类,提供了一系列静态方法来创建各种常见配置的线程池,简化了创建线程池的使用但是会带来一些问题,很多开发规范里都不建议大家直接使用。
    • 生产环境中建议谨慎使用或直接使用ThreadPoolExecutor构造函数自定义。

线程池原理

任务管理:

  • 任务调度
  • 任务缓冲。不同的队列可以实现不一样的任务存取策略。
  • 任务申请
  • 任务拒绝。JDK提供的四种已有拒绝策略

线程管理:

  • 创建
    • Worker这个工作线程,实现了Runnable接口。并持有一个线程thread,一个初始化的任务firstTask。
    • thread是在调用构造方法时通过ThreadFactory来创建的线程
    • firstTask。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。
  • 回收
    • 线程长时间不运行的时候进行回收。线程池使用一张Hash表去持有线程的引用,这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。将其引用消除可被JVM自动的回收。
    • Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。
  • 增加
  • 执行
相关推荐
better_liang5 天前
每日Java面试场景题知识点之-线程池
java·线程池·并发编程·juc·企业级开发
tkevinjd7 天前
JUC5(线程池)
java·线程池·多线程·juc
报错小能手13 天前
线程池学习(二)线程池详解
c++·线程池
六bring个六15 天前
自实现线程池
c++·线程池
Da Da 泓16 天前
多线程(七)【线程池】
java·开发语言·线程池·多线程
特立独行的猫a1 个月前
C++使用Boost的Asio库优雅实现定时器与线程池工具类
开发语言·c++·线程池·定时器·boost·asio
deng-c-f1 个月前
Linux C/C++ 学习日记(49):线程池
c++·学习·线程池
长路 ㅤ   1 个月前
ForkJoinPool.commonPool()实现
线程池·java并发·forkjoinpool·工作窃取·commonpool
努力发光的程序员1 个月前
互联网大厂Java求职面试实录
java·jvm·线程池·多线程·hashmap·juc·arraylist