线程池ThreadPoolExecutor体检套餐

线程池的核心参数解释

如何设置线程池配置

线程池的执行流程

拒绝策略

线程池执行任务前后追加一些操作

哪些场景用?

1、线程池的核心参数解释
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
七个核心参数:
corePoolSize:核心线程数 (线程池内部运行起来之后,最少有多少个线程等活, 核心线程是
懒加载
maximumPoolSize:最大线程数 (当工作队列堆满了,新任务进来就创建创建非核心线程)
keepAliveTime:最大空闲时间 (默认的非核心线程,没任务之后,只能空闲这么久,时间到了就要干掉)
unit:空闲时间单位(keepAliveTime的单位)七种取值:
1 TimeUnit.DAYS;               //天
2 TimeUnit.HOURS;             //小时
3 TimeUnit.MINUTES;           //分钟
4 TimeUnit.SECONDS;           //秒
5 TimeUnit.MILLISECONDS;      //毫秒
6 TimeUnit.MICROSECONDS;      //微妙
7 TimeUnit.NANOSECONDS;       //纳秒
workQueue:工作队列 (当核心线程数足够后,加入的任务会扔到这个工作队列存储,
一般用LinkedBlockingQueue)
threadFactory:线程工厂(构建线程的,参考阿里规范,要求线程一定得给有意义的名
字,方便错误排查)
handler:拒绝策略 (核心数到了,队列也满了,非核心数也满了,再进来的任务,走这个策略)
1.1 线程处理完任务之后,会在工作队列的位置执行take(就是一直等) 、 poll 指定等待的时间)等任务.
1.2 非核心线程创建完之后,是马上处理传递过来的任务,而不是优先处理工作队列的任务...
1.3 线程池在创建工作线程时,会区分非核心线程和核心线程,但是线程开始工作后,就不会区分了,只保证数值足够就可以
2、如何设置线程池配置?
上面内7个参数,具体如何配置?
 核心线程数最重要,用线程池的目的,就是为了更好的发挥CPU的性能,提升业务执行的效率,对吧?
所以为了配置核心线程数,需要关注以下内容:
硬件的CPU内核数,
业务类型:
  是CPU密集
  还是IO密集
 或是混合型(一半CPU,一半IO)
CPU密集型:就是需要CPU一直调度当前线程,当前线程做的业务大多数是计算类,数据转换类的,不会出现阻塞的情况。一般核心线程就设置CPU内核数,或者加减1也可以。
由于CPU厂商不同,性能也不同,加上服务器的操作系统区别,这样设置不一定是最佳选择,需进行科学的压测才能得出一个合理数字(比如用 jmeter压测)
IO密集型:比如系统的业务涉及到了大量的查询数据库,查询三方服务获取一 些数据,而查询数据的时候,线程基本都处理阻塞状态。
这种查询三方服务或者是数据库的操作,有可能会因三方服务网络抖动,或者查询没有走索引等愿意,阻塞时间会有点长,此时你会发现,IO密集型方式,似乎没有什么特别好的公式可以直接用。
想获取合理的数值,你可以优先根据IO密集和CPU密集大致得出一个核心线程数,基于这个数值
做压测,根据测试的结果,你可以调大核心线程数,重测,调小核心线程数重新测,直到得出一个效率最高的数。
压测的过程中,需要动态修改线程池中的参数,而线程池恰恰可以做到动态的修改,只需要执行set 方法即可,可以自行实现。也可以用一些三方的开源框架(美团开源的程池监控工具Hippo4j)
最大线程数: 其实核心线程数已经可以做到尽可能的发挥CPU的性能了,所以最大线程数最好设置 为跟核心线程数一致。如果在核心线程的基础上,又多追加了几个线程,反而会导致性能下降~
工作队列: 是任务排队的地方,很多任务会扔到这个队列中排队,等待线程执行。
每个任务都是Runnable的实现,是一个对象,对象要占用堆内存空间。不能让排队的任务压爆JVM 内存。
任务扔到工作队列,需要等待排队处理,可以考虑排在最后面的任务需要多久才能处理到。
再根据业务允许的延迟时间考虑工作队列要多长。
拒绝策略:
当工作队列满了,如果最大线程数 = 核心线程,那就要走拒绝策略.
如果任务是个记录日志啊这种丢弃也无所谓的任务,那就直接丢弃
如果任务是核心业务线必备的一环,那就不能扔,可以让业务线程处理,也可以把任务留存好,做最终一致性。
如果性能还是存在瓶颈,只能加服务器资源了。。
3、线程池的执行流程
1、当任务加入线程池之后,先看核心线程数是否满了,没满就去构建核心线程处理任务。
2、若核心线程数满了,就将任务加入工作队列,继续排队。
3、任务加入工作队列时,如果工作队列也满了,就尝试创建非核心线程处理任务。
4、如果非核心线程也创建失败(到最大线程数),直接执行拒绝策略。
4、常见的拒绝策略有哪些?

线程池自己提供了4种拒绝策略
AbortPolicy:直接异常。
DiscardPolicy:任务直接放弃。
CallerRunsPolicy:哪个投递的任务,就继续由谁自己处理。
DiscardOldestPolicy:将队列中排在最前面的任务踢出去,尝试将自己再次投递到线程池。
如果这4个不够,拒绝策略提供了一个接口规范,也可以自己去实现 。
5、如何在线程池执行任务前后追加一些操作?
线程池底层在执行任务前后,有两个勾子函数\扩展口,我们可以继承线程池对这两个方法重写,
以后只要线程池执行任务前后,都会执行这两个勾子函数:
public class MyTP extends ThreadPoolExecutor {
public MyTP(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
}
@Override protected void beforeExecute(Thread t, Runnable r) {
// 任务执行前干点啥。
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
// 任务执行后干点啥。
}
}
6、什么场景用到线程池
1、要异步处理的,比如发邮件,发短信,就用线程池。
2、如定时任务,时间一到,需要触发一个线程去执行某个任务,也能线程池,JUC也给咋们提供了 ScheduleThreadPoolExecutor,就是定时任务的线程池。
3、同时访问多个服务希望能做并行处理,提升处理效率,就可以用线程数
4、处理的数据体量很大,比如做导入导出的业务,可以用多线程做并行处理,可以提升处理效率。
5、其实java框架底层都有线程池,只是我们没配置,比如RabbitMQ的消费者,你不配置线程的信息,他就是单线程处理,配置了,就是多个消费者并行处理,速度很快。
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值