为什么用线程池?
当资源的数量不够用的时候,可以采用 池化技术,如线程池,数据库连接池
复用线程,减少创建销毁线程的资源消耗,提升性能
提高响应速度,有任务时不用等线程创建就能立即执行
方便管理,统一调度调优监控
----------------------------------------
线程池参数
corePoolSize 核心线程数,默认线程空闲也不会回收
maxinumPoolSize 最大线程数,当核心线程数不够用时,就创建临时线程,但线程池里的线程总数不能超过最大线程数
keepAliveTime 线程空闲存活时间
workQueue 任务队列
threadFactory 生产线程的工厂,可以使用默认的,或自定义线程工厂
rejectedExecutionHandler 任务拒绝策略
----------------------------------------
拒绝任务的两种情况
1.线程池已经调用了 shutdown 等方法关闭了
2.最大线程数已满
----------------------------------------
任务拒绝策略
AbortPolicy 拒绝策略
线程池默认策略,拒绝新任务,同时抛异常 RejectedExecutionException
DiscardPolicy 抛弃策略,丢弃任务
DiscardOldestPolicy 抛弃最老任务策略
抛弃队头任务,再入队
CallerRunsPolicy 调用者执行策略
用提交任务的线程执行任务
自定义策略
----------------------------------------
线程池的5种状态
-----------------------------
running
线程池创建以后的初始状态,
正常处理任务
-----------------------------
shutdown
调用了 shutdown() 后,状态从 running 变为 shutdown
不再接收新任务,会将任务队列中的任务执行完
-----------------------------
stop
调用了 shutdownnow() 后,状态从 running/shutdown 变为 stop
将会终止工作线程,清空任务队列
-----------------------------
tidying
所有工作线程已停止,工作队列清空以后,状态从 stop 变为 tidying
将会执行 terminated() 钩子方法
-----------------------------
terminated
执行完 terminated() 后,状态从 tidying 变为 terminated
----------------------------------------
线程池任务调度流程
线程池添加任务
如果核心线程数未满,就创建核心线程执行任务
如果核心线程数已满,任务队列未满,将任务放入任务队列中
如果任务队列已满,未达到最大线程数,创建临时线程执行,临时线程忙完之后,达到空闲存活时间就回收
如果达到最大线程数,拒绝处理
----------------------------------------
线程池中的线程复用原理
线程池里的线程会不断的从任务队列中获取任务来执行,执行一个任务时调用任务的 run() 方法,然后又执行下一个任务调用任务的 run() 方法,如此循环,直到空闲
----------------------------------------
线程池中阻塞队列的作用?
接收任务,没有任务时挂起获取任务的线程,有任务时唤醒挂起的线程
----------------------------------------
BlockingQueue
---------------------
boolean offer(E e);
往 BlockingQueue 里加入数据, 成功返回 true, 失败返回 false
---------------------
boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException;
如果在指定的时间内,不能往队列中加入数据,则返回失败
---------------------
void put(E e) throws InterruptedException;
往 BlockingQueue 里加入数据, 如果 BlockQueue 满了, 阻塞调用该方法的线程,
直到 BlockingQueue 里面有空间再唤醒
---------------------
E poll(long timeout, TimeUnit unit)
throws InterruptedException;
从 BlockingQueue 取出一个队首的对象,在指定时间内没有数据可取,返回失败
---------------------
E take() throws InterruptedException;
取走 lockingQueue 里排在首位的对象, 若BlockingQueue为空, 阻塞进入等待状态,
直到 BlockingQueue 有新的数据被加入
---------------------
LinkedBlockingQueue
链表实现的阻塞队列,按FIFO排序任务
适合并发程度 高 的情况下使用
---------------------
ArrayBlockingQueue
数组实现的有界队列,创建时设置大小,按FIFO排序任务
适合并发程度 低 的情况下使用
---------------------
DelayQueue - 无界阻塞延迟队列
队列中每个元素都有过期时间,只有过期的元素才能出队
底层是 PriorityBlockingQueue,对头的元素是过期最快的元素
---------------------
SynchronousQueue
同步队列,不存储元素的阻塞队列,插入元素的线程必须等到另一个线程移除操作,否则插入操作一直阻塞
适合生产者处理能力和消费者处理能力相差不大的情况下使用
----------------------------------------
Executors 提供四种线程池
newSingleThreadExecutor 单线程线程池
一个线程一个阻塞队列,
适合多个任务按顺序执行的场景
无界队列(LinkedBlockingQueue)如果任务提交速度长期大于任务处理速度会导致队列堆积大量任务内存溢出
newFixedThreadPool 定长线程池
提交任务时,
线程数没有达到固定数量,就创建一个新线程
达到固定数量就保持不变,如果某个线程执行异常而结束,线程池会补充一个新线程
如果没有空闲线程,新任务会进入阻塞队列
适合长期稳定的任务,可通过固定线程数防止 cpu 被长时间过度消耗
无界队列(LinkedBlockingQueue)如果任务提交速度长期大于任务处理速度会导致队列堆积大量任务内存溢出
newCachedThreadPool 可缓存线程池
提交任务时,没有空闲线程,就创建一个
及时回收空闲线程
适合处理突发性强耗时较短的任务场景
SynchronousQueue 同步队列
最大线程数无穷大,大量任务会产生大量的线程导致内存溢出或耗尽cpu
newScheduledThreadPool 可调度线程池,支持延时及周期性任务执行
DelayQueue - 无界阻塞延迟队列
最大线程数无穷大,大量任务会产生大量的线程导致内存溢出或耗尽cpu
大厂不允许使用 Executors 创建线程池,要求使用标准构造器 ThreadPoolExecutor 创建线程池
线程数配置
公用配置
corePoolSize 和 maxinumPoolSize 保持一致,有新任务时没有空闲线程就创建新线程,而不是放在队列里等线程空闲
使用有界队列,根据具体情况配长度,以测试为准
LinkedBlockingQueue(128)
keepAliveTime 30s
allowCoreThreadTimeOut(true)
IO 密集型任务
为提高 cpu 利用率,线程数配 cpu 核心数的2倍
CPU 密集型任务
线程数等于CPU核心数,减少切换线程消耗时间
混合型任务
最佳线程数 = ((线程等待时间 + 线程CPU时间)/ 线程CPU时间) * CPU核心数