线程池(状态/参数/底层原理)
2021-02-25 23:11:46 2 举报
AI智能生成
线程池(状态/参数/底层原理)
作者其他创作
大纲/内容
为什么要用线程池?
线程复用,减少频繁创建/销毁线程的系统开销
提高响应速度,当任务到达时,不需要创建就能立即执行
控制最大并发数,方便管理线程,防止资源不足
何时使用线程池?
单个任务处理时间比较短
需要处理的任务数量很大
线程池底层原理
工作原理
工作线程数未达到核心线程数时,新建线程执行任务
工作线程数达到核心线程数且阻塞队列未满,把任务放入阻塞队列
阻塞队列已满,且工作线程数小于最大线程数时,新建线程执行任务
工作线程数达到了最大线程数时,采用拒绝策略来处理,默认抛异常
threadPoolExecutor.execute(...)
1、任务非空校验
2、判断并添加核心线程执行任务
3、判断并进行任务入队列(二次校验)
4、添加普通线程执行任务,如果失败则执行拒绝策略
AddWork()
调用位置
当工作线程数 < 核心线程数的时候,addWorker(command, true)添加核心线程
double check的时候,如果发现线程池处于running状态但里面没有工作线程,
则添加个空任务和一个普通线程,即创建了一个新的线程去阻塞队列中拿任务
则添加个空任务和一个普通线程,即创建了一个新的线程去阻塞队列中拿任务
队列已满时,创建非核心线程执行,如果失败了则执行拒绝策略
作用
第一个for循环:判断当前的线程池状态是否能接受新任务
第二个for循环:判断线程池中当前的工作线程数量
尝试通过CAS方式增加工作线程数workerCount
如果增加成功,则跳出这个双循环,往下执行
启动一个工作线程,开启一个独占锁
runWorker()
getTask()
获取任务
threadPoolExecutor.allowCoreThreadTimeOut(true):允许核心线程超时销毁
task.run()
运行任务
线程池状态
负数用补码表示:取反+1
高3位记录线程池运行状态
低29位记录当前工作线程数
五种
RUNNING:接收新任务,并处理队列任务
SHUTDOWN:不接收新任务,但处理现有队列中的任务(调用pool.shutdown()方法)
STOP:不接收新任务,也不处理队列任务,并且会中断正在处理的任务(pool.shutdownNow())
TIDYING:所有线程已终止,工作线程数为0,TIDYING 状态时会执行钩子函数 terminated()
TERMINATED:线程池彻底终止的状态
线程池参数
corePoolSize
核心线程数,默认线程池中没线程,等任务来了才创建线程
如调用了预创建线程prestartAllCoreThreads(),线程池会提前创建并启动所有核心线程
maximumPoolSize
如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务
如果是无界队列则这个参数没有效果
如果是无界队列则这个参数没有效果
keepAliveTime
线程存活时间,该参数只有在线程数大于 corePoolSize 时才会生效
unit
keepAliveTime 的单位。TimeUnit
workQueue
ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO
LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO,但是不设置大小时就是 Integer.MAX_VALUE
SynchronousQueue:不存储元素的阻塞队列,每次插入都必须等待上一个移出
PriorityBlockingQueue:一个具有优先级的无界阻塞队列
threadFactory
顾名思义,创建线程的工厂,可以给线程重命名
handler
JDK原生拒绝策略
AbortPolicy:中止策略,直接抛出拒绝执行的异常,也是默认策略(注意处理好抛出的异常)
CallerRunsPolicy:调用者运行策略,用调用者所在的线程来执行任务
DiscardOldestPolicy:弃老策略,丢弃阻塞队列中等待最久的任务,并执行当前任务
DiscardPolicy:丢弃策略,直接丢弃当前任务,不触发任何动作
扩展第三方拒绝策略
Dubbo中线程池
输出了一条警告级别的日志,日志内容为线程池的详细设置参数,
以及线程池当前的状态,还有当前拒绝任务的一些详细信息
以及线程池当前的状态,还有当前拒绝任务的一些详细信息
输出当前线程堆栈详情,帮助进一步排查,定位问题
抛出拒绝执行异常,使本次任务失败,继承自ThreadPoolExecutor.AbortPolicy
Netty中线程池
Netty中的实现很像JDK中的CallerRunsPolicy,舍不得丢弃任务
与CallerRunsPolicy不同的是,Netty是新建了一个线程来处理的
Netty在创建线程时未做任何的判断约束,除非new不出线程才会抛异常
常见问题
使用线程池的风险?
线程池自己引发的死锁
所有正在执行的线程都在等待阻塞队列中阻塞线程的结果
线程泄露
问题:线程抛出异常而没有被捕捉到时,或者永远等待用户输入的线程
解决:要么只给予它们自己的线程,要么只让它们等待有限的时间
资源不足
避免线程池过大造成资源浪费,适当的调整线程池的大小
如何高效的使用线程池?
不要对那些同步等待其它任务结果的任务排队
给可能需要很长时间的操作要指定最长等待时间
理解任务,有效地调整线程池大小
Executor 和 Executors 的区别?
Executor 是接口,Executors 是工具类
Executors
Executors.newFixedThreadPool(10);
创建一个定长线程池,可控制线程最大
并发数,超出的线程会在队列中等待
创建一个定长线程池,可控制线程最大
并发数,超出的线程会在队列中等待
CorePoolSize: n
MaximumPoolSize: n
keepAliveTime: 0
LinkedBlockingQueue
MaximumPoolSize: n
keepAliveTime: 0
LinkedBlockingQueue
只有10个核心线程工作
使用无界队列
Executors.newCachedThreadPool();
创建一个可缓存线程池,如果线程池长
度超过处理需要,可灵活回收空闲线程,
若无可回收,则新建线程
创建一个可缓存线程池,如果线程池长
度超过处理需要,可灵活回收空闲线程,
若无可回收,则新建线程
CorePoolSize: 0
MaximumPoolSize: max
keepAliveTime: 60L
SynchronousQueue
MaximumPoolSize: max
keepAliveTime: 60L
SynchronousQueue
没有核心线程,都是临时线程
最大线程数是Integer.Max_Value
Executors.newSingleThreadExecutor();
创建一个单线程化的线程池,它只会用唯
一的工作线程来执行任务, 保证所有任务
按照指定顺序(FIFO, LIFO, 优先级)执行
创建一个单线程化的线程池,它只会用唯
一的工作线程来执行任务, 保证所有任务
按照指定顺序(FIFO, LIFO, 优先级)执行
CorePoolSize: 1
MaximumPoolSize: 1
keepAliveTime: 0
LinkedBlockingQueue
MaximumPoolSize: 1
keepAliveTime: 0
LinkedBlockingQueue
只有一个核心线程工作
使用无界队列
为什么不推荐使用以上3种线程池?
因为newFixedThreadPool和newSingleThreadExecutor使用的是LinkedBlockingQueue
而newCachedThreadPool的线程数又无限大,容易引发内存溢出的问题
而newCachedThreadPool的线程数又无限大,容易引发内存溢出的问题
生产环境如何合理配置线程池?
点击跳转 👉
如果线上突然宕机,线程池中队列的请求怎么办?
数据会丢失, 可以在提交任务到队列前在数据库中插入一条任务信息,并更新其状态,
系统重启后启动后台线程去数据库中扫描未提交的任务信息重新添加到线程池中
系统重启后启动后台线程去数据库中扫描未提交的任务信息重新添加到线程池中
0 条评论
下一页
为你推荐
查看更多