线程池

2020/10/21 posted in  Java

线程池是什么

线程池是一种基于池化思想来管理线程的工具。
使用线程池的好处:

  • 降低资源消耗:通过池化技术复用已经创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可执行。
  • 提高线程的可管理性:使用线程池进行统一的分配、调优和监控。
  • 提供更多更强大的功能:比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池解决的问题

线程池解决的核心问题就是资源管理问题。
1、频繁申请、销毁资源和调度资源,将带来额外的消耗,可能非常巨大。
2、对资源无限申请,缺少抑制手段,易引发系统资源耗尽的风险。
3、系统无法合理管理内部的资源分布,会降低系统的稳定性。

线程池运行机制

16023122966589
16023122966589

ThreadPoolExecutor构造方法 ``` public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; } ```

这几个核心参数的作用:

  • corePoolSize:核心线程数,为线程池的基本大小。核心线程不会被回收,即使没有任务执行,也会保持空闲状态。
  • maximumPoolSize:为线程池最大线程大小。当线程运行数量达到corePoolSize,且workQueue队列塞满任务了之后,继续创建线程。
  • keepAliveTime 和 unit :是超过corePoolSize之后的“临时线程”的存活时间。
  • workQueue:用于存放任务的阻塞队列。当前运行线程超过核心线程数时,新的任务会处于等待状态,存在workQueue中,BlockingQueue是一个阻塞式队列,相当于一个容器,生产者往里面放资源,消费者从里面拿资源。
  • handler:当队列和最大线程池都满了之后的饱和策略。

阻塞队列:

16023150102703
16023150102703

首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。

  • 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  • 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  • 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  • 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
    其执行流程如下图所示:

16023179404989
16023179404989

线程运行状态:

16023123528952
16023123528952

拒绝策略

16023245427842
16023245427842

创建线程池的四种方式

newCachedThreadPool

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}

通过源码可以返现,这种方式创建的线程池,其核心线程数为0,最大线程数为 Integer 的最大值,线程存活时间 60s,还有这个 SynchronousQueue,这个队列没有存储空间,有任务提交后就必须去执行。

当有任务提交后,会去看有没有还存活的空闲线程,有的话让线程去执行任务,没有存活线程的话会立即创建线程执行该任务

弊端:
并发量大了后线程创建的太多,导致 OOM

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}

核心线程数与最大线程数一样,空闲线程存活时间为0,有序队列。

当有任务提交时,该线程池始终以核心线程去执行,核心线程满了后进入队列等待,FIFO

弊端:
因为队列容量无限大,可能会出现请求太多,出现 OOM

newScheduledThreadPool

可以延迟处理,或者定时处理

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

核心线程数可以指定,最大线程数是 Integer 最大值,空闲线程存存活时间为0,

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

最大线程数与核心线程数都是1,队列有序无限大,

可能导致OOM

线程池大小分配

  • IO 密集型
    指的是 CPU 运算能力较好
    CPU 核心数 * 2

  • CPU 密集型
    指的是,CPU 运算能力较差
    CPU 个数 +1

  • 混合型

  • 任务类型不同

--
现有一个线程池,参数corePoolSize = 5,maximumPoolSize = 10,BlockingQueue阻塞队列长度为5,此时有4个任务同时进来,问:线程池会创建几条线程?

如果4个任务还没处理完,这时又同时进来2个任务,问:线程池又会创建几条线程还是不会创建?

如果前面6个任务还是没有处理完,这时又同时进来5个任务,问:线程池又会创建几条线程还是不会创建?

--
https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
http://objcoding.com/2019/04/11/threadpool-initial-parameters/
https://www.cnblogs.com/frankyou/p/10135212.html
https://www.cnblogs.com/NiceCui/p/11929396.html