为什么使用线程池?

Java线程

线程是调度CPU的最小单元,也叫轻量级进程LWP(Light Weight Process)

线程分类

image.png
image.png

  • 用户级线程(User-Level Thread,简称:ULT)

    用户程序实现,不依赖操作系统核心,应用提供创建\同步\调度和管理线程的函数来控制用户进程.不需要用户态\内核态切换,速度快,内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞

  • 内核级线程(Kernel-level Thread,简称:KLT)

    系统内核管理线程(KLT),内核保持线程的状态和上下文信息,线程阻塞不会引起程序阻塞.在多处理器操作系统上,多线程在多处理器上并行运行.线程的创建\调度和管理由内核完成,效率比ULT要慢,比进程操作快

JVM虚拟机使用的是KLT
划分的原因:保证系统内核的安全,避免用户直接操作内核
image.png

普通线程创建执行过程

image.png 多线程下,会根据时间片算法来分配线程的执行,例如现有两个线程,线程一的时间片用完了,下一个时间片是线程二要执行,线程一的上下文信息(指令\程序缓存\中间数据)就要保存到内核空间的TSS任务状态段,下次拿到时间片的时候再从TSS任务状态段中重载上下文继续执行,而且java是依赖于内核级线程,会在用户态和内核态进行切换

但这样会产生一个问题,在高并发的情况下,不断地创建线程和用户态与内核态切换,会消耗大量的资源,导致系统崩溃
线程池就是来解决这个问题的,复用线程,减少线程的创建

线程池的意义

线程是稀缺资源,它的创建与销毁是比较重且耗资源的操作.而Java线程依赖于内核线程,创建线程需要进行操作系统状态切换,为避免资源过度消耗需要设法重用线程执行多个任务.线程池就是一个线程缓存,负责对线程进行统一分配\调优与监控

什么时候使用线程池?

  1. 单个任务处理时间比较短
  2. 需要处理的任务数量很大

    线程池的优势

  • 重用存在的线程,减少线程创建,消亡的开销,提高性能
  • 提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行
  • 提高线程的可管理性,线程是稀缺资源,如果无限的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配\调优和监控

    实现方式

image.png

Executors线程池创建工具(不推荐)

这个类提供了5种创建线程池的方法,它只是jdk提供的线程池的工具类,封装了线程池的创建方式,本质它还是使用后面讲的那种方式进行创建的

  1. newFixedThreadPool 创建固定线程数量的线程池
  2. newSingleThreadExecuror 创建只有一个线程的线程池
  3. newCachedThreadPool 创建n个线程的线程池(可缓存)

最好是手动创建线程池

ThreadPoolExecutor

这里主要使用ThreadPoolExecutor这个类创建线程池

构造函数主要参数

  1. corePoolSize: 核心线程数量
  2. maximumPoolSize: 最大临时线程数(含核心线程)
  3. keepAliveTime: 临时线程不使用后销毁延时
  4. unit: 延时时间单位
  5. workQueue(线程安全): 核心线程没有及时处理的东西先放到这个阻塞队列,如果满了就会开启最大线程,使用最大线程中创建的临时线程,
    在任意时刻,不管并发有多高,永远只有一个线程能够进行队列的入队或者出队操作
  6. threadFactory: 线程工厂
  7. rejectedExecutionHandler: 拒绝策略,即核心线程,阻塞队列,最大临时线程都处于负载的情况下,如果还有任务进来就会触发拒绝策略
    image.png
    1.AbortPolicy: 抛弃任务,并且会抛出异常(默认)
    2.DiscardPolicy: 既不处理任务,也不抛出异常
    3.DiscardOldestPolicy: 丢弃队列最前面的任务
    4.CallerRunsPolicy: 调用者回退策略,将任务给调用者处理

    工作原理图

    image.png

    线程池生命状态

    image.png
    Running: 能接收新任务,以及处理已经添加的任务
    Shutdown: 不接收新任务,可以处理已经添加的任务
    Stop: 不接受新任务,不处理已经添加的任务,并且中断正在处理的任务
    Tidying:所有任务已经终止,ctl记录的任务数量为”0”(ctl负责记录线程池的运行状态与活动线程数)
    Terminated:线程池彻底终止.则线程池转化为terminated状态

    分析源码

    execute()方法[将任务添加到对应的地方]

    image.png

    worker的工作原理与线程池关闭过程

如何合理配置线程池大小

这是一个可变的,没有一个具体合理配置方案,因为这个只能根据系统环境而定,不同的配置有不同的配置方案