最后
最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
一起互勉~
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
- CPU 密集型程序
- I/O 密集型程序
CPU 密集型程序
一个完整请求,I/O操作可以在很短时间内完成, CPU还有很多运算要处理,也就是说 CPU 计算的比例占很大一部分
假如我们要计算 1+2+…100亿 的总和,很明显,这就是一个 CPU 密集型程序
在【单核】CPU下,如果我们创建 4 个线程来分段计算,即:
-
线程1计算
[1,25亿)
-
… 以此类推
-
线程4计算
[75亿,100亿]
我们来看下图他们会发生什么?
由于是单核 CPU,所有线程都在等待 CPU 时间片。按照理想情况来看,四个线程执行的时间总和与一个线程5独自完成是相等的,实际上我们还忽略了四个线程上下文切换的开销
所以,单核CPU处理CPU密集型程序,这种情况并不太适合使用多线程
此时如果在 4 核CPU下,同样创建四个线程来分段计算,看看会发生什么?
每个线程都有 CPU 来运行,并不会发生等待 CPU 时间片的情况,也没有线程切换的开销。理论情况来看效率提升了 4 倍
所以,如果是多核CPU 处理 CPU 密集型程序,我们完全可以最大化的利用 CPU 核心数,应用并发编程来提高效率
I/O密集型程序
与 CPU 密集型程序相对,一个完整请求,CPU运算操作完成之后还有很多 I/O 操作要做,也就是说 I/O 操作占比很大部分
我们都知道在进行 I/O 操作时,CPU是空闲状态,所以我们要最大化的利用 CPU,不能让其是空闲状态
同样在单核 CPU 的情况下:
从上图中可以看出,每个线程都执行了相同长度的 CPU 耗时和 I/O 耗时,如果你将上面的图多画几个周期,CPU操作耗时固定,将 I/O 操作耗时变为 CPU 耗时的 3 倍,你会发现,CPU又有空闲了,这时你就可以新建线程 4,来继续最大化的利用 CPU。
综上两种情况我们可以做出这样的总结:
线程等待时间所占比例越高,需要越多线程;线程CPU时间所占比例越高,需要越少线程。
到这里,相信你已经知道第一个【正确】使用多线程的场景了,那创建多少个线程是正确的呢?
创建多少个线程合适?
面试如果问到这个问题,这可是对你理论和实践的统考。想完全答对,你必须要【精通/精通/精通】小学算术
从上面知道,我们有 CPU 密集型和 I/O 密集型两个场景,不同的场景当然需要的线程数也就不一样了
CPU 密集型程序创建多少个线程合适?
有些同学早已经发现,对于 CPU 密集型来说,理论上 线程数量 = CPU 核数(逻辑)
就可以了,但是实际上,数量一般会设置为 CPU 核数(逻辑)+ 1
, 为什么呢?
《Java并发编程实战》这么说:
计算(CPU)密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。
所以对于CPU密集型程序, CPU 核数(逻辑)+ 1
个线程数是比较好的经验值的原因了
I/O密集型程序创建多少个线程合适?
上面已经让大家按照图多画几个周期(你可以动手将I/O耗时与CPU耗时比例调大,比如6倍或7倍),这样你就会得到一个结论,对于 I/O 密集型程序:
最佳线程数 =
(1/CPU利用率)
=1 + (I/O耗时/CPU耗时)
我这么体贴,当然担心有些同学不理解这个公式,我们将上图的比例手动带入到上面的公式中:
这是一个CPU核心的最佳线程数,如果多个核心,那么 I/O 密集型程序的最佳线程数就是:
最佳线程数 =
CPU核心数
*(1/CPU利用率)
=CPU核心数
*(1 + (I/O耗时/CPU耗时))
说到这,有些同学可能有疑问了,要计算 I/O 密集型程序,是要知道 CPU 利用率的,如果我不知道这些,那要怎样给出一个初始值呢?
按照上面公式,假如几乎全是 I/O耗时,所以纯理论你就可以说是 2N(N=CPU核数),当然也有说 2N + 1的,(我猜这个 1 也是 backup),没有找到具体的推倒过程,在【并发编程实战-8.2章节】截图在此,大家有兴趣的可以自己看看
理论上来说,理论上来说,理论上来说,这样就能达到 CPU 100% 的利用率
如果理论都好用,那就用不着实践了,也就更不会有调优的事出现了。不过在初始阶段,我们确实可以按照这个理论之作为伪标准, 毕竟差也可能不会差太多,这样调优也会更好一些
谈完理论,咱们说点实际的,公式我看懂了(定性阶段结束),但是我有两个疑问:
- 我怎么知道具体的 I/O耗时和CPU耗时呢?
- 怎么查看CPU利用率?
没错,我们需要定量分析了
幸运的是,我们并不是第一个吃螃蟹的仔儿,其实有很多 APM (Application Performance Manager)工具可以帮我们得到准确的数据,学会使用这类工具,也就可以结合理论,在调优的过程得到更优的线程个数了。我这里简单列举几个,具体使用哪一个,具体应用还需要你自己去调研选择,受篇幅限制,暂不展开讨论了
- SkyWalking
- CAT
- zipkin
上面了解了基本的理论知识,那面试有可能问什么?又可能会以怎样的方式提问呢?
面试小问
小问一
假设要求一个系统的 TPS(Transaction Per Second 或者 Task Per Second)至少为20,然后假设每个Transaction由一个线程完成,继续假设平均每个线程处理一个Transaction的时间为4s
如何设计线程个数,使得可以在1s内处理完20个Transaction?
但是,但是,这是因为没有考虑到CPU数目。家里又没矿,一般服务器的CPU核数为16或者32,如果有80个线程,那么肯定会带来太多不必要的线程上下文切换开销(希望这句话你可以主动说出来),这就需要调优了,来做到最佳 balance
小问二
计算操作需要5ms,DB操作需要 100ms,对于一台 8个CPU的服务器,怎么设置线程数呢?
如果不知道请拿三年级期末考试题重新做(今天晚自习留下来),答案是:
线程数 = 8 * (1 + 100/5) = 168 (个)
那如果DB的 QPS(Query Per Second)上限是1000,此时这个线程数又该设置为多大呢?
同样,这是没有考虑 CPU 数目,接下来就又是细节调优的阶段了
因为一次请求不仅仅包括 CPU 和 I/O操作,具体的调优过程还要考虑内存资源,网络等具体内容
增加 CPU 核数一定能解决问题吗?
看到这,有些同学可能会认为,即便我算出了理论线程数,但实际CPU核数不够,会带来线程上下文切换的开销,所以下一步就需要增加 CPU 核数,那我们盲目的增加 CPU 核数就一定能解决问题吗?
在讲互斥锁的内容是,我故意遗留了一个知识:
怎么理解这个公式呢?
题外话
我们见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了7、8年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
注意:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!