初识 Java 并发,这些问题你懂了吗?

449c56afb8594421bd0f28f595178338.png

大家好,我是树义。Java 并发是每个 Java 工程师需要掌握的技能。今天分享一篇小龙的文章,其用面试的场景为我们介绍了并发编程可能遇到的问题,场景、并发编程的挑战以及解决方案等。

面试现场

叮叮叮......

面试官:“你好,我是XX面试官,请问是小龙吗?”

小龙:“您好,面试官,我是小龙”

面试官:“好的,现在有空吗,我们开始面试吧”

小龙:“嗯嗯,准备好啦”

.......

other questions

.......

面试官:“你对并发这块有了解吗?”

小龙:“嗯呢,并发这块很重要,我研究过相关源码,同时也有自己的一些理解与看法。”

面试官:“好的,我们不谈原理哈。首先,你能简单说说何为多线程吗?谈谈你对多线程的理解?

小龙:“我们普遍认为的多线程通俗一点讲指的就是在单个程序中可以同时运行多个不同的线程,执行不同的任务。”

独白:“我们先来点大家都晓得的,然后再整点虚的,每天一个装B 小技巧~”

小龙:“如果往本质上分析,追根朔源,我们最根本目标是为了让我们的程序项目降低延迟,提高吞吐量,而要做到这,我们一般可以通过优化算法(这就是为何大厂要考察算法与数据结构的原因),二一个广泛的讲就是尽可能将硬件的性能发挥到极致。”

小龙:“而计算机硬件主要考虑的就是 I/O,CPU,而并发编程,便是可以提升硬件的利用率,再具体点来说,就是提升 I/O 的利用率和 CPU 的利用率从而提高程序的性能(降低延迟,提高吞吐量)

小龙:“再准确一点便是,合理利用多线程去提高 CPU 和 I/O 的综合利用率。”

独白:“回复【面试笔记】,拿捏面试官~”

面试官:“你能举几个你平时项目中哪些场景下使用到多线程呢?”

小龙:“最常见的就是多个客户端访问一个 web 程序啦,服务器处理不同的请求。”

小龙:“除此之外,平时项目中我用的比较多的就是,比如项目另外开一个线程进行异步处理,又比如进行分布式计算这些,另外用的比较多的就是另开线程进行后台任务的处理等等”

面试官:“好的。那,我们都知道并发编程一般是为了让程序降低延迟,提高吞吐量,那是不是我开的线程越多越好呢?平时你是怎么考虑的?”

小龙:“肯定不是线程越多越好啦,如果希望通过多线程执行任务让程序运行得更快,我们还得考虑上下文切换、死锁、各种资源限制的问题。”

小龙:“在项目可能还会遇到死锁等问题,有些时候为了更好的管理线程,充分利用资源会使用线程池。”

面试官:“平时有遇到这些问题吗?比如,各种线程池的线程数量,Tomcat 的线程数、Jdbc 连接池的连接数设置这些,你是如何考虑设置多少才合适呢?”

独白:“又是这些问烂了的问题。”

独白:“其实我从来不管这些,凭直觉设置,或者公司环境都给你配好了。还是给你整点虚的吧~”

小龙:“说实话,平时我们可能更多是凭借经验设置,比如线程池的配置,一般会按照经验配置一个,然后随时关注线程池大小对程序的影响即可。但是,我还是想给你扯点虚的(方便过面试)”

小龙:"这个要看多线程具体的应用场景。由于我们程序一般都是 CPU 计算和 I/O 操作交叉执行的,我们一般分为 I/O 密集型计算CPU 密集型计算。"

CPU 密集型计算:大部分场景下都是纯 CPU 计算

I/O 密集型计算:大部分情况,I/O 操作执行的时间相对于 CPU 计算来说都非常长

小龙:“对于 CPU 密集型,由于主要是纯 CPU 计算,我们需要尽可能提高 CPU 利用率,因此理论 线程的数量 =CPU 核数 就是最合适的"

小龙:“不过这只是理想情况,实际项目中是线程可能偶尔会出现阻塞什么的,这时可以多备用一个线程作为替补,因此 线程的数量一般会设置为“CPU 核数 +1

小龙:“ 而对于 I/O 密集型,由于大部分情况都是 I/O 操作执行时间,我们可以尽可能去协调 I/O 与 CPU 执行时间,那么可以在某个线程执行完 I/O 操作后,其他线程恰好也执行完了各自的 CPU 操作,这样便尽可能的去利用了CPU。”

小龙:“也就是说如果 CPU 计算I/O 操作的耗时是 1:1,那么 2 个线程是最合适的。然后我们再提出万能式。”

最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

独白:“学废了吗?每天一个装 B 小技巧,面试就是要给面试官虚虚实实,虚实结合。更多装逼、虚扯小技巧,尽在【面试笔记】,后台回复噢。”

面试官:“牛逼,之前我听你说了死锁也可能造成一些问题,能给我谈谈你对死锁的理解吗?”

小龙:“先举个经典的死锁例子,便是线程 A 持锁 a,又要获取锁b;线程 B 持锁 b,又要获取锁 a,就会死锁。”

小龙:“结合业务分析,比如我 线程 A 从 X 账户向 Y 账户转账,线程 B 从账户 Y 向账户 X 转账,为了确保转账可靠,肯定需要对转账账户和收账账户对象都要加锁,那么此时便会出现上述的情况,就会发生死锁。”

面试官:“嗯嗯,那对于死锁问题,你是怎样解决的呢?”

独白:“幸好在【面试笔记】这些都详细总结过,又给他整点虚的~”

小龙:“ 解决死锁问题最好的办法还是规避死锁。在实际项目中,若想要检测是否因为死锁导致程序性能下降,我们可以利用 JDK 的一些工具命令。”

小龙:“1、利用 jps、ps、任务管理器获取进程ID”

小龙:“2、利用 Jstack pid 获取进程中线程堆栈信息(区分线程状态->查看等待目标->对比 Monitor等持有状态)

小龙:”3、利用 Jconsole、Jvisualvm 排查死锁问题(java监视管理平台)”

小龙:“一般打破造成死锁条件(互斥、占有并等待、非抢占、循环等待)就可以避免死锁,平时我们程序应该尽量按顺序获取锁,使用超时锁、不要同时获取多把锁。”

面试官:“OK,今天就聊这些基础,下期再剖析并发机制底层实现原理吧~”

独白:“不愧是我,真男人是也!【面试笔记】在手,大厂 offer 不愁”

知识总结

本期我们通过面试模拟初步探讨了并发编程的一些基础问题,下期会继续深入剖析 并发机制底层实现原理。订阅+星标持续追更。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值