线程池是什么
线程池是一种基于池化思想来管理线程的工具。
使用线程池的好处:
- 降低资源消耗:通过池化技术复用已经创建的线程,降低线程创建和销毁造成的损耗。
- 提高响应速度:任务到达时,无需等待线程创建即可执行。
- 提高线程的可管理性:使用线程池进行统一的分配、调优和监控。
- 提供更多更强大的功能:比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
线程池解决的问题
线程池解决的核心问题就是资源管理问题。
1、频繁申请、销毁资源和调度资源,将带来额外的消耗,可能非常巨大。
2、对资源无限申请,缺少抑制手段,易引发系统资源耗尽的风险。
3、系统无法合理管理内部的资源分布,会降低系统的稳定性。
线程池运行机制
ThreadPoolExecutor构造方法
``` public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue这几个核心参数的作用:
- corePoolSize:核心线程数,为线程池的基本大小。核心线程不会被回收,即使没有任务执行,也会保持空闲状态。
- maximumPoolSize:为线程池最大线程大小。当线程运行数量达到corePoolSize,且workQueue队列塞满任务了之后,继续创建线程。
- keepAliveTime 和 unit :是超过corePoolSize之后的“临时线程”的存活时间。
- workQueue:用于存放任务的阻塞队列。当前运行线程超过核心线程数时,新的任务会处于等待状态,存在workQueue中,BlockingQueue是一个阻塞式队列,相当于一个容器,生产者往里面放资源,消费者从里面拿资源。
- handler:当队列和最大线程池都满了之后的饱和策略。
阻塞队列:
首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
- 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
- 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
- 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
- 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
其执行流程如下图所示:
线程运行状态:
拒绝策略
创建线程池的四种方式
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