多线程-线程池解析

507 阅读11分钟

线程池类结构

image.png

线程池分类

ThreadPoolExecutor

预定义线程池

预定义线程池是一种基于ThreadPoolExecutor的按照不同概念的构建的线程池

  • FixedThreadPool
  • CachedThreadPool
  • SingleThreadExecutor
  • ScheduledThreadPool
  • SingleThreadScheduledExecutor

线程池创建方式

通过ThreadPoolExecutor构造方法创建自定义线程池

  • corePoolSize→核心线程数
  • maximumPoolSize→最大线程数
  • keepAliveTime→空闲线程的存活时间
  • unit→空闲线程的存活时间的时间单位
  • workQueue→工作队列/阻塞队列
  • handle→饱和策略/拒绝策略
  • threadFactory→线程工场

通过Executors工具类创建预定义线程池

  • newFixedThreadPool→创建FixedThreadPool线程池
  • newCachedThreadPool→创建CachedThreadPool线程池
  • newSingleThreadExecutor→创建newCachedThreadPool线程池
  • newScheduledThreadPool→创建newScheduledThreadPool线程池
  • newSingleThreadScheduledExecutor→创建ScheduledThreadPoolExecutor线程池

线程池原理

image_1.png

线程池状态

image_2.png

RUNNING

线程池创建时的默认状态,此状态下线程池可以接受新任务并处理已添加的任务

SHUTDOWN

此状态下线程池不接受新任务但处理已添加的任务,RUNNING状态下可以通过调用shutdown方法切换到此状态

STOP

此状态下线程池不接受新任务并且不处理已添加的任务,RUNNING和SHUTDOWN状态下可以通过调用shutdownNow方法切换到此状态

TIDYING

线程池所有任务完成将切换到此状态,SHUTDOWN状态下队列和任务都为空以及STOP状态下任务为空会自动切换到此状态

TERMINATED

线程池终止时将切换到此状态,TIDYING状态下执行terminated方法会切换到此状态

ThreadPoolExecutor参数解析

corePoolSize

corePoolSize 指的是核心线程数,线程池初始化时线程数默认为 0,当有新的任务提交后,会创建新线程执行任务,如果不做特殊设置,此后线程数通常不会再小于 corePoolSize ,因为它们是核心线程,即便未来可能没有可执行的任务也不会被销毁

maximumPoolSize

随着任务量的增加,在任务队列满了之后,线程池会进一步创建新线程,最多可以达到maximumPoolSize 来应对任务多的场景,如果未来线程有空闲,大于 corePoolSize 的线程会按照设置的存活时间被合理回收。所以正常情况下,线程池中的线程数量会处在 corePoolSize 与 maximumPoolSize 的闭区间内

keepAliveTime+unit

当线程池中线程数量多于核心线程数时,而此时又没有任务可做,线程池就会检测线程的 keepAliveTime,如果超过规定的时间,空闲的线程就会被销毁,以便减少内存的占用和资源消耗。如果后期任务又多了起来,线程池也会根据规则重新创建线程,所以这是一个动态的过程,也可以用 setKeepAliveTime 方法动态改变 keepAliveTime 的参数值

threadFactory

threadFactory的作用是生产线程以便执行任务。我们可以选择使用默认的线程工厂,创建的线程都会在同一个线程组,并拥有一样的优先级,且都不是守护线程,我们也可以选择自己定制线程工厂,以方便给线程自定义命名,不同的线程池内的线程通常会根据具体业务来定制不同的线程名

workQueue

阻塞队列类结构

image_3.png

预定义线程与队列对应关系

线程池队列
FixedThreadPoolLinkedBlockingQueue
SingleThreadExecutorLinkedBlockingQueue
CachedThreadPoolSynchronousQueue
ScheduledThreadPoolDelayedWorkQueue
SingleThreadScheduledExecutorDelayedWorkQueue

LinkedBlockingQueue

基于链表的无界队列,适用于FixedThreadPool和SingleThreadExecutor线程池,实际最大值为Integer#MAX_VALUE,由于 FixedThreadPool 和SingleThreadExecutor线程池的线程数是固定的,所以没有办法增加特别多的线程来处理任务,这时就需要 LinkedBlockingQueue 这样一个没有容量限制的阻塞队列来存放任务

SynchronousQueue

SynchronousQueue没有容量,是无缓冲等待队列,所以没有一个地方来暂存元素,导致每次取数据都要先阻塞,直到有数据被放入;每次放数据的时候也会阻塞,直到有消费者来取

DelayedWorkQueue

DelayedWorkQueue 的特点是内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是Heap的数据结构。之所以线程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 选择 DelayedWorkQueue,是因为它们本身正是基于时间执行任务的,而延迟队列正好可以把任务按时间进行排序,方便任务的执行

ArrayBlockingQueue

基于数组的有界队列,的最大特点就是容量是有限且固定的。这样一来,使用 ArrayBlockingQueue 且设置了合理大小的最大线程数的线程池,在任务队列放满了以后,如果线程数也已经达到了最大值,那么线程池根据规则就会拒绝新提交的任务,而不会无限增加任务或者线程数导致内存不足,可以非常有效地防止资源耗尽的情况发生

PriorityBlockingQueue

基于优先级的无界阻塞队列,可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则,可以自动扩容,队列永远不会满

handle

拒绝策略原理

在线程池运行时,在某些情况下会触发拒绝策略;

  • 第一种情况是当我们调用 shutdown 等方法关闭线程池后,即便此时线程池内依然有没执行完的任务正在执行,但由于线程池已经关闭,此时如果再向线程池内提交任务,就会遭到拒绝。
  • 第二种情况是线程池没有能力继续处理新提交的任务,也就是工作已经非常饱和的时候;例如新建一个线程池,使用容量上限为 20的 ArrayBlockingQueue 作为任务队列,并且指定线程池的核心线程数为 5,最大线程数为 10,假设此时有 30 个耗时任务被提交,在这种情况下,线程池会首先创建核心数量的线程,也就是5个线程来执行任务,然后往队列里去放任务,队列的 10 个容量被放满了之后,会继续创建新线程,直到达到最大线程数 10。此时线程池中一共有 30 个任务,其中 10 个任务正在被 10 个线程执行,还有 20 个任务在任务队列中等待,而且由于线程池的最大线程数量就是 10,所以已经不能再增加更多的线程来帮忙处理任务了,这就意味着此时线程池工作饱和,这个时候再提交新任务时就会被拒绝触发拒绝策略

拒绝策略类结构

image_4.png

AbortPolicy

默认策略,此策略会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。

CallerRunsPolicy

当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。

DiscardOldestPolicy

如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险

DiscardPolicy

当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务

预定义线程池解析

FixedThreadPool

固定线程池,主要体现在核心线程数等同于最大线程数,所以不存在空闲线程因此空闲时间为0MS

核心线程数最大线程数存活时间工作线程线程工场拒绝策略
自定义同核心线程数0MSLinkedBlockingQueueDefaultThreadFactoryAbortPolicy
/**
 * 创建方法1:一个线程数参数,可指定核心线程数与最大线程数
 */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

/**
 * 创建方法2:一个线程数参数,可指定核心线程数与最大线程数;一个线程池工场参数
 */
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}

CachedThreadPool

缓存线程池,核心线程数为0,最大线程数为Integer.MAX_VALUE,空闲线程为60秒

核心线程数最大线程数存活时间工作线程线程工场拒绝策略
0Integer.MAX_VALUE60SSynchronousQueueDefaultThreadFactoryAbortPolicy
/**
 * 创建方法1:无参数
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

/**
 * 创建方法2:一个线程工场参数
 */
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}

SingleThreadExecutor

单线程数线程池,核心线程数和最大线程数均为1,空闲线程立即销毁

核心线程数最大线程数存活时间工作线程线程工场拒绝策略
110MSLinkedBlockingQueueDefaultThreadFactoryAbortPolicy
/**
 * 创建方法1:无参数
 */
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

/**
 * 创建方法2:一个线程工场参数
 */
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}

ScheduledThreadPool

定时执行的线程池,核心线程数自定义,最大线程数为Integer.MAX_VALUE,空闲线程10毫秒后销毁

核心线程数最大线程数存活时间工作线程线程工场拒绝策略
自定义Integer.MAX_VALUE10MSDelayedWorkQueueDefaultThreadFactoryAbortPolicy
/**
 * 创建方法1: 一个线程数参数,可指定核心线程数
 */
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

/**
 * 创建方法2: 一个线程数参数,可指定核心线程数;一个线程工场参数
 */
public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory);
}

SingleThreadScheduledExecutor

定时执行的单线程线程池,核心线程数和最大线程数均为1,空闲线程10毫秒后销毁

核心线程数最大线程数存活时间工作线程线程工场拒绝策略
1Integer.MAX_VALUE10MSDelayedWorkQueueDefaultThreadFactoryAbortPolicy
/**
 * 创建方法1: 一个线程数参数,可指定核心线程数
 */
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

/**
 * 创建方法2: 一个线程数参数,可指定核心线程数;一个线程工场参数
 */
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1, threadFactory));
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory);
}

ThreadPoolExecutor常用配置参数

corePoolSize核心线程数

  1. CPU密集型:例如加密、解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务。对这样的任务一般设置的线程数为 CPU 核心数的 1~2 倍
  2. IO密集型:例如数据库、文件的读写,网络通信等任务,这种任务的特点是并不会特别消耗 CPU 资源,但是 IO 操作很耗时,总体会占用比较多的时间。对于这种任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费;反之如果设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少,可以更好地利用资源
  3. 计算公式:线程数 = CPU 核心数 *(1+平均等待时间/平均工作时间)

handle拒绝策略

  1. 对于强一致性的业务来说,需要使用默认的AbortPolicy,通过异常捕获进行业务的补偿
  2. 对于允许任务失败的业务来说,可以使用CallerRunsPolicy直接抛弃新添加的任务
  3. 对于允许任务丢失的业务来说,可以使用DiscardOldestPolicy删除阻塞队列中的队首节点以腾出空间添加新任务

线程池提交任务方法

submit

  • 参数:可以接收Callable类型的任务,也可以接收Runnable类型的任务
  • 返回值:有Future类型的返回值,可以通过Future.get()获取结果,但是因为Runnable的返回值为void,所以结果均为null
  • 异常捕获:submit内部会捕获任务执行异常,并可以通过Future.get()获取异常信息
  • 适用场景:适合关心任务执行结果希望根据执行情况进一步处理的任务作业

excute

  • 参数:只可以接收Runnable类型的任务
  • 返回值:无返回值
  • 异常捕获:如果任务发生异常,将会抛出
  • 适用:适合不关系任务执行结果的任务作业