线程池工作原理
Posted 翊梦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程池工作原理相关的知识,希望对你有一定的参考价值。
一、线程池工作流程
创建一个线程池,ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 10, TimeUnit.SECONDS, new ArrayBlockingQueue(10), new ThreadPoolExecutor.AbortPolicy());
一文读懂线程池的工作原理(故事白话文)
(给ImportNew加星标,提高Java技能)
前言
本文以程序员做需求的例子,比喻线程池的工作过程。以故事白话的方式展开,跟大家阐述线程池工作原理,以方便大家更好理解线程池,谢谢阅读哈~
-
什么是线程池? -
什么是核心线程? -
什么是阻塞队列? -
什么是非核心线程? -
什么是空闲存活时间? -
什么是饱和策略? -
线程池工作原理流程图&源码概览。
什么是线程池?
小田螺勤勤恳恳,任劳任怨,夜以继日地工作着。终于有一天,他晋升为公司的主管,负责公司日常业务。
风轻云淡的一天,老板找到了小田螺,“我们公司员工越来越多了,我想搞个员工管理系统,你那边安排一下哈,要在一个月后完成。” 小田螺拍拍胸口,没问题!
因为当前公司还没有程序员,所以小田螺快马加鞭打开接单网站,提交员工管理系统需求。等待不久,开发者(名字,线程A) 接单,谈好合同,开始开发,系统交付……一系列流程下来,并且一个月过后,一个五脏俱全的员工管理系统终于完成了。老板对此大加赞赏~
过了不久,老板再次发话,“公司越来越多人迟到了,我们再搞个考勤系统吧!" 小田螺接到任务,马上又开始上网,提需求找人开发,这次来了线程 B 接单......
逝者如斯。月底了,老板又提出开发个薪酬系统需求……小田螺听了头皮发麻,one day day 的,重复去网上找人开发!“为了节省成本,不如我们雇佣几个程序员(线程a、b、c),成立自己的 IT 技术部门吧!我们就管 IT 部门叫线程池吧!”老板听了,一拍即合。
线程池就是管理线程的池子,当有任务要处理时,不用频繁创建新线程,而是从池子拿个线程出来处理。当任务执行完,线程并不会被销毁,而是在等待下一个任务。因此可以节省资源,提高响应速度。
什么是核心线程?
线程池IT部门成立后,雇佣了几个与公司有正式合同关系的员工a、b、c。小田螺管他们几个正式员工做核心线程。当老板提一个需求过来,小田螺就把需求分配给手上没活干的线程处理。
什么是阻塞队列?
一天早上,老板睡眼惺忪。来到公司后,一口气提了四个需求,a、b、c 按顺领完任务后,发现还剩余一个需求任务。这个怎么安排呢?难道又去网上找人吗?成立了线程池 IT 部门,还去找人(找线程干活),会被人笑落大牙的!
聪明的小田螺想到一个好办法。我们可以搞个 DPMS 需求池,把还没分配的需求,放进待完成的 DPMS 需求池里面吧,等到 a、b、c谁先干完活,再把这个任务领走。这个 DPMS 需求池,我们给它取名阻塞队列,英文就叫 WorkQueue!
什么是非核心线程?
又在一个晴空万里的午后,老板喝了一杯咖啡,闲来没事,就跑去阻塞队列(DPMS 需求池)看看。一看就傻了!需求池堆积了几十个需求,排期都是满满的了。老板马上叫小田螺进来办公室,商量如何处理这些需求任务。
“要不,我们雇佣多几个员工(搞多几个核心线程)?”,小田螺建议。 “不行不行,公司财务「开销」有点大!”
“要不然,我们要求业务提少点任务需求?(请求少点)”,小田螺又建议。“你是不是傻?!请求少了,不是自断财路吗?你回家想想办法先吧!”老板放大了他的嗓门~
小田螺回家闭目让神,每天早早就睡觉,两耳不闻窗外事……终于有一天,在一个梦香里,他想到了一个好办法:“老板,我们可以去别的公司(外包公司)雇佣几个员工(假设名字叫 d、e、f、g)一段时间,让它们来做 DPMS 需求池(阻塞队列) 里面的需求。等做完需求,再派他们回去就好啦。”
老板一听就乐了,这个方案好。心里美滋滋,需求的活有人干了,公司财务又省钱,两全其美呀~ 这几个派遣来的外包员工(d、e、f、g),就把它叫做非核心线程吧。
什么是空闲时间?
自从来了 d、e、f、g 外包员工(非核心线程),老板长舒一口气。这么多活,终于有人干了。
但是又有一天,到了7点所谓的下班时间,老板走出办公室,发现线程池 IT 部门的员工,都走得七七八八了。心里一怒!这帮粉肠,怎么一到下班时间就跑,工作这么不饱和了?他随手点进 DPMS 需求池,才发现原来需求都被做完了……还有一堆外包同事(非核心线程)要发工资呢,这波亏大了~
第二天,小田螺被秘密叫进了老板办公室。既然 DPMS 需求池都已经没需求了。我们准备派外包同事(非核心线程)回去吧?但是呢一般,需求一没有,就马上让他们回去(线程回收),如果需求一下子又来,就有点 hold 不住了……
“要不酱紫,我们等需求池空的时候,隔个15天还是10天,再让外包同事(非核心线程)回去吧?” 这里定义的15天或者10天,就是线程空闲存活时间啦。
什么是饱和策略?
在临近双11的时候,不仅老板提了很多需求,新来的运营小姐姐们也提了好多好多的需求。新需求如源头活水,滚滚的来~
首先呢,线程池 IT 部门 a、b、c 三个正式员工(核心线程)都忙于处理需求(请求)。接着,DPMS 需求池(阻塞队列)也被挤满了。最后呢,连 d、e、f、g 外包同事(非核心线程)也忙得不可开交。
这时候需求还是做不完,怎么办呢?双11赶着上线呢?小田螺愁眉苦脸,从潮起愁到潮落……
没办法了,只能动用饱和策略啦。像是丢弃需求任务、抛异常告诉老板别加需求了、丢弃需求池最老的需求任务、或者交给提需求的人自己处理。
最后老板决定,拒绝再提新的需求。于是线程池 IT 部门又可以正常运行了~
线程池的饱和策略事件,主要有四种类型:
AbortPolicy:抛出一个异常,默认值; DiscardPolicy:新提交的任务直接被抛弃; DiscardOldestPolicy:丢弃队列里最老的任务,将当前这个任务继续提交给线程池; CallerRunsPolicy:交给线程池调用所在的线程进行处理,即将某些任务退回给调用者。
线程池工作原理流程图 & 源码概览
故事讲完啦,再复习下线程池工作流程图吧~
有兴趣的朋友,源码也可以看下哈~
if (command == null) {
throw new NullPointerException();
}
int c = ctl.get();
// 判断当前活跃线程数是否小于corePoolSize
if (workerCountOf(c) < corePoolSize) {
// 如果小于,则调用addWorker创建线程执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 如果大于等于corePoolSize,则将任务添加到workQueue队列。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) {
reject(command);
}
else if (workerCountOf(recheck) == 0) {
addWorker(null, false);
}
}
// 如果放入 workQueue 队列失败,则创建非核心线程执行任务
else if (!addWorker(command, false)) {
//(如果这时创建线程失败(当前线程数大于等于maximumPoolSize时)) 调用reject拒绝接受任务
reject(command);
}
GitHub:https://github.com/whx123/JavaHome
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
好文章,我在看❤️
以上是关于线程池工作原理的主要内容,如果未能解决你的问题,请参考以下文章