漫画:怎么给女朋友讲明白线程池?


作者?| Isabella
责编 | 伍杏玲
线程池是Java面试必问问题之一!
有没有对源码滚瓜烂熟的童鞋?请举手!??♂(怎么没人举手。。)
对了,今天先来撒一波狗狼~
?(表打我~)
来,介绍下:

她叫码妞,是我码仔的女朋友喔!
她也在学习各类前端技术,可厉害了!
大家鼓掌欢迎吧!以后她会经常来问我问题的,要被烦了~
?
最近码妞也在看Java线程池呢,已经看得一头雾水了,正准备去问问码仔,
看码仔能不能给她讲明白了!







线程
线程是一种资源,并不是只存在程序的世界里。
程序,本来就是对生活的一种抽象表述。
比如像车站的售票窗口、退票窗口、检票窗口,每个窗口都在做不同的事情,就是车站里同时运行着的不同线程。
线程多了,需要管理,不同的线程也要能保证不会互相干扰,各做各的。


线程的生命周期
这个图很熟悉的吧~


好,开始讲线程池啦~

ThreadPoolExecutor
线程池源码里最主要的类了~
看下开头的这段注释:
/**
?*?The?main?pool?control?state,?ctl,?is?an?atomic?integer?packing
?*?two?conceptual?fields
?*?workerCount,?indicating?the?effective?number?of?threads
?*?runState,?indicating?whether?running,?shutting?down?etc
?*
?*?The?runState?provides?the?main?lifecycle?control,?taking?on?values:看到英文就头晕?没事啦~
主要讲线程池重要的两个状态:
runState:线程池运行状态
workerCount:工作线程的数量
@ReachabilitySensitive
private?final?AtomicInteger?ctl?=?new?AtomicInteger(ctlOf(RUNNING,?0));
private?static?final?int?COUNT_BITS?=?Integer.SIZE?-?3;
private?static?final?int?CAPACITY?=?(1?< 1;
//?runState?is?stored?in?the?high-order?bits
private?static?final?int?RUNNING?=?-1?< private?static?final?int?SHUTDOWN?=?0?< private?static?final?int?STOP?=?1?< private?static?final?int?TIDYING?=?2?< private?static?final?int?TERMINATED?=?3?<
线程池用一个32位的int来同时保存runState和workerCount,其中高3位(第31到29位)是runState,其余29位是workerCount(大约500 million)。
来看看存储结构(码仔手动画的哦
)

它的构造方法:
public?ThreadPoolExecutor(int?corePoolSize,
?????int?maximumPoolSize,
?????long?keepAliveTime,
?????TimeUnit?unit,
?????BlockingQueue?workQueue,
?????ThreadFactory?threadFactory,
?????RejectedExecutionHandler?handler) ?{
????????...
}


corePoolSize
maximumPoolSize
keepAliveTime
workQueue
任务阻塞队列。通过workQueue,线程池实现了阻塞功能。当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。
threadFactory
创建线程的工厂。所有的线程都是通过这个Factory创建的。
默认会使用
Executors.defaultThreadFactory() 来作线程工厂。
handler 线程池的饱和策略。做不了任务了找理由罢工?

核心线程数,好比班干部的人数。
最大线程数,好比教室里的座位数。当提交任务数超过了这个最大值,线程还有拒绝策略——RejectExecutionHandler,做不动了嘛。
除核心线程外的空闲线程保持存活时间。当线程池里线程数超过corePoolSize数量了,keepAliveTime时间到,就把空闲线程关了,不然也闲置了呀,节省能量嘛。
AbortPolicy
直接抛出异常,默认策略;
CallerRunsPolicy
用调用者所在的线程来执行任务;
DiscardOldestPolicy
丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy
直接丢弃任务。



Worker来了!

你看Worker的定义,其实它就是封装了的工作线程~
private?final?class?Worker
?????extends?AbstractQueuedSynchronizer
?????implements?Runnable
Worker既实现了Runnable,又继承了AbstractQueuedSynchronizer(AQS),所以它既是一个可执行的任务,又可以达到锁的效果。


看看Worker构造方法:
/**
?*?Creates?with?given?first?task?and?thread?from?ThreadFactory.
?*?@param?firstTask?the?first?task?(null?if?none)
?*/
?Worker(Runnable?firstTask)?{
?????setState(-1);?//?inhibit?interrupts?until?runWorker
?????this.firstTask?=?firstTask;
?????this.thread?=?getThreadFactory().newThread(this);
?}





线程池是怎么工作的?
DuangDuangDuang!


public?void?execute(Runnable?command)?{
?????if?(command?==?null)
?????????throw?new?NullPointerException();
?/*
?*?Proceed?in?3?steps:
?*
?*?1.?If?fewer?than?corePoolSize?threads?are?running,?try?to
?*?start?a?new?thread?with?the?given?command?as?its?first
?*?task.?The?call?to?addWorker?atomically?checks?runState?and
?*?workerCount,?and?so?prevents?false?alarms?that?would?add
?*?threads?when?it?shouldn't,?by?returning?false.
?*?2.?If?a?task?can?be?successfully?queued,?then?we?still?need
?*?to?double-check?whether?we?should?have?added?a?thread
?*?(because?existing?ones?died?since?last?checking)?or?that
?*?the?pool?shut?down?since?entry?into?this?method.?So?we
?*?recheck?state?and?if?necessary?roll?back?the?enqueuing?if
?*?stopped,?or?start?a?new?thread?if?there?are?none.
?*?3.?If?we?cannot?queue?task,?then?we?try?to?add?a?new
?*?thread.?If?it?fails,?we?know?we?are?shut?down?or?saturated
?*?and?so?reject?the?task.
?*/
?????int?c?=?ctl.get();
?????if?(workerCountOf(c)?< ?corePoolSize)?{
?????????if?(addWorker(command,?true))
?????????????return;
????????c?=?ctl.get();
?????}
?????/**
?????*?2、如果线程池RUNNING状态,且入队列成功
?????*/
?????if?(isRunning(c)?&&?workQueue.offer(command))?{
?????????int?recheck?=?ctl.get();
?????//如果再次校验过程中,线程池不是RUNNING状态,
?????//?并且remove(command)--workQueue.remove()成功,拒绝当前command
?????????if?(!?isRunning(recheck)?&&?remove(command))
?????????????reject(command);
?????????//为什么只检查运行的worker数量是不是0呢??为什么不和corePoolSize比较呢??
?????????//?只保证有一个worker线程可以从queue中获取任务执行就行了??
?????????//?因为只要还有活动的worker线程,就可以消费workerQueue中的任务
?????????else?if?(workerCountOf(recheck)?==?0)
?????????????addWorker(null,?false);
?????????}
?????????/**
?????????*?3、如果线程池不是running状态?或者?无法入队列
?????????*?尝试开启新线程,扩容至maxPoolSize,
?????????*?如果addWork(command,?false)?失败了,拒绝当前command
?????????*/
?????????else?if?(!addWorker(command,?false))
?????????????reject(command);
?????????}}
看execute方法里的注释,一步步说得很清楚。
如果当前正在运行的线程数 < corePoolSize,尝试用给到的command来启动一个新线程作为第一个任务。
调用addWorker方法,检查runState和workerCount,并且如果增加线程的话,能防止产生错误警报,如果不能增加线程,则返回false。如果一个任务被成功地加到队列里,仍然需要双重检验来确认是否需要新建一个线程。
(因为可能在上一次检查后,已经存在的线程已经died)或者进入这个方法后,线程池已经被关闭了。所以我们需要再次检查state,如果线程池停止了需要回滚入队列,如果池中没有线程了,新建一个线程。
如果不能把任务加入队列(可能线程池已经关闭或者满了),那么需要新开一个线程(往maxPoolSize发展)。如果失败的话,说明线程池shutdown了或者满了,就要拒绝这个任务了。


给你流程图!










工具类 Executors
线程池里还有个重要的类:Executors。
Executors是一个Java中的工具类,它提供工厂方法来创建不同类型的线程池。
用它可以很方便地创建出下面几种线程池来:

ExecutorService?singleService?=?Executors.newSingleThreadExecutor();
ExecutorService?fixedService?=?Executors.newFixedThreadPool(9);
ExecutorService?cacheService?=?Executors.newCacheThreadPool();
或者通过ThreadPoolExecutor的构造函数自定义需要的线程池。

作者简介:公众号码个蛋,码上养成好习惯。


为码一代,想教码二代却无从下手:
听说少儿编程很火,可它有哪些好处呢?
孩子多大开始学习比较好呢?又该如何学习呢?
最新的编程教育政策又有哪些呢?
下面给大家介绍CSDN新成员:极客宝宝(ID:geek_baby)
戳他了解更多↓↓↓

?热 文?推 荐?
?敲诈团伙将黑手伸向宅男, 你在家看不可描述的视频, 竟被骗走100万美元!

关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 中国经济向世界提供“机遇清单” 7904443
- 2 再次提醒:中国公民近期避免前往日本 7808959
- 3 狂甩45次 无汞体温计才降到36℃ 7712650
- 4 2025这些“经济”持续成长壮大 7618878
- 5 女生被宿舍孤立关门外一夜?学校回应 7519864
- 6 “无名”男子医院躺7年半 警方介入 7423717
- 7 网警:男子AI生成车展低俗视频被拘 7334254
- 8 5000万粉丝女网红涉嫌逃税仍直播 7232758
- 9 北冥有鱼竟然是真的 7135435
- 10 寒潮来袭!多地气温将创下半年来新低 7045541

程序人生
