什么是线程池?
答: 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程(提高线程复用,减少性能开销)。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中然后等待下一次分配任务。
为什么要用线程池?(线程池的好处)
答: 1. 利用线程池管理和复用线程,控制并发数。
2. 实现任务队列缓存策略和拒绝机制。
3. 实现与时间相关的功能,如 定时执行,周期执行等。
线程池状态
线程池和线程一样拥有自己的状态,在ThreadPoolExecutor类中定义了一个volatile变量runState来表示线程池的状态,线程池有四种状态,分别为RUNNING、SHURDOWN、STOP、TERMINATED。
线程池底层执行原理
线程池源码
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
int corePoolSize (core:核心的) = > 表示常驻核心线程数
如果等于0,则任务执行完之后,没有任何请求进入时销毁线程池的线程;
如果大于0,即使本地任务执行完毕,核心线程也不会被销毁.
这个值的设置非常关键;
设置过大会浪费资源;
设置过小会导致线程频繁地创建或销毁.
int maximumPoolSize = > 表示线程池能够容纳同时执行的最大线程数
最大线程数>=1
如果待执行的线程数大于此值,需要借助第5个参数的帮助,缓存在队列中.
如果maximumPoolSize = corePoolSize 即是固定大小线程池.
最大线程数=核心线程数+非核心线程数
long keepAliveTime = > 表示线程池中的线程空闲时间
当空闲时间达到keepAliveTime时,线程会被销毁,直到只剩下corePoolSize个线程;
避免浪费内存和句柄资源.
在默认情况下,当线程池的线程数大于 corePoolSize 时 keepAliveTime 才起作用.
但是当 ThreadPoolExecutor的 allCoreThreadTimeOut =True 时核心线程超时后也会被回收.
TimeUnit unit 表示时间单位
keepAliveTime的时间单位通常是TimeUnit.SECONDS.
BlockingQueue
当请求的线程数大于maximumPoolSize时,线程进入BlockingQueue.
后续示例代码中使用的LinkedBlockingQueue是单向链表,使用锁来控制入队和出队的原子性;
两个锁分别控制元素的添加和获取,是一个生产消费模型队列.
ThreadFactory threadFactory 表示线程工厂
它用来生产一组相同任务的线程;
线程池的命名是通过给这个factory增加组名前缀来实现的.
在虚拟机栈分析时,就可以知道线程任务是由哪个线程工厂产生的.
RejectedExecutionHandler handler
当超过第5个参数workQueue
的任务缓存区上限的时候,就可以通过该策略处理请求,这是一种简单的限流保护.
友好的拒绝策略可以是如下三种:
(1) 保存到数据库进行削峰填谷;在空闲时再提取出来执行
(2)转向某个提示页面
(3)打印日志
AbortPolicy:丢弃任务,抛出 RejectedExecutionException
CallerRunsPolicy:只用调用者所在线程来运行任务,有反馈机制,使任务提交的速度变慢)。
DiscardOldestPolicy
若没有发生shutdown,尝试丢弃队列里最近的一个任务,并执行当前任务, 丢弃任务缓存队列中最老的任务,并且尝试重新提交新的任务
DiscardPolicy:不处理,丢弃掉, 拒绝执行,不抛异常
当然,也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略.如记录日志或持久化存储不能处理的任务
注:自己学习总结的过程,有什么不好的地方,可以评论留言,互相学习。