多线程

为什么要用线程池?核心参数有哪些

作用

  • 降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗。
  • 提高响应速度;任务来了直接有线程可用可执行,而不是先创建线程,再执行。
  • 提高线程的可管理;线程是稀缺资源,使用线程池可用统一分配调优监控。

线程池创建

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue) {

    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
        Executors.defaultThreadFactory(), defaultHandler);

}

核心参数

corePoolSize:核心线程最大数量,即线程池中常驻线程的最大数量。线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程;如果超过了corePoolSize则新建的是非核心线程。

maximumPoolSize:线程池中运行的最大线程数,包括核心线程和非核心线程。

keepAliveTime:线程池中空闲线程所存活的最长时间(仅适用于非核心线程)。

unit:表示超出核心线程数之外的线程的空闲存活时间,也就是核心线程不会消除,但是超出核心线程数的部分线程如果空闲一定的时间则会被消除,可以通过keepAliveTime来设置空闲时间。

workQueue:用来存放任务的阻塞队列,假设我们现在核心线程都被使用,还有任务进来则全部放入队列,直到整个队列被放满但再持续进入则会创建新的线程。

ThreadFactory:实际上是一个线程工厂,用来生产线程执行任务,我们可以选择使用默认的创建工厂,产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择自定义线程工厂,一般会根据业务来制定不同的线程工厂。

Handler:任务拒绝策略,有两种情况,第一种是当我们调用shutdown等方法关闭线程池后,这时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续想线程池提交任务就会遭到拒绝,另一种情况就是当达到最大线程数,线程池已经没有能力继续处理新提交的任务时,这时也就会拒绝。

执行流程图

  1. 判断线程池中的核心线程数是否已经到达corePoolSize,没有则创建一个核心线程执行任务。
  2. 若核心线程数已经到达corePoolSize,判断阻塞队列workQueque是否已满,如果没满,则将新任务加入阻塞队列。如果已满,判断线程池中的线程数是否到达maximumPoolSize,如果没有,则新建一个非核心线程执行任务,如果到达阀值,则执行线程池饱和策略。

线程池饱和策略

  1. AbortPolicy:直接抛出一个异常阻止系统正常运行,默认策略
  2. DiscardPolicy:直接丢弃任务,不予任何处理也不抛异常(如果允许任务丢失,建议此方案)
  3. DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
  4. CallerRunsPolicy:调用者运行一种调节机制,该策略既不会抛弃任务也不会抛异常,而是将某些任务回退到调用者,从而降低新任务的流量。