【2024Java面试突击】并发编程、线程池面试实战

前言

最近在更新面试突击专栏,我把每一篇将字数都尽量控制在 2000 字以内,可能在文章里边写的没有那么细致,主要是提供一些 问题 以及 回答的思路 ,以及 面试中可能忽略的漏洞 ,所以在看完文章之后,如果自己简历中有这方面的内容的话,一定要认真去整理一份自己的回答,并且多查阅相关资料,如果看的文章少,就会导致学习到的内容太片面

并发编程

面试官为什么都喜欢问并发编程的问题?

如果面试的大一点的公司,用户量上来之后,那么并发包下的东西还是很容易会用到的,并且写代码时,如果对并发安全不算了解,那可能写完的代码存在许多并发上的问题,可能测试的时候没问题,到生产环境中造成严重后果!

我之前面试过唯品会,唯品会的面试官给我的印象就是很在乎你的基础,无论是并发、JVM、MySQL、Redis 原理,还是项目中使用到的技术,都会问你底层原理,我面试之后也问面试官了,为什么偏向于去问这么多技术的底层原理,面试官给的回答是因为只有了解底层的原理,你在使用的过程中才会更加注意他存在哪方面的问题,可以更好的去避免!

说说synchronized关键字的底层原理是什么?

下面来用 大白话 说一下原理:

synchronized 保证线程同步主要是依赖于两个 jvm 的指令:monitorenter、monitorexit 来实现的,比如说 synchronized 修饰一个代码块,那么进入代码块之前,执行 monitorenter 表示上锁,退出代码块之后,执行 monitorexit 表示解锁,以此来保证不同线程顺序执行这个代码块

并且 synchronized 在 jdk1.6 进行了优化,将锁分为了四种状态:无锁、偏向锁、轻量级锁、重量级锁,这 4 个状态会随着竞争激烈而逐渐升级,不过偏向锁在 jdk15 之后逐渐废弃,因为维护的开销比较大

能聊聊你对CAS的理解以及其底层实现原理可以吗?

CAS 操作需要 3 个参数:要写入的内存地址、预期值、要写入的值

CAS 的原理就是,去要写入的内存地址判断,如果这个值等于预期值,那么就在这个位置上写上要写入的值

CAS 存在一些缺陷:

循环时间过长:如果 CAS 自旋一直不成功,会给 CPU 带来很大开销
只能针对一个共享变量
存在 ABA 问题:CAS 只检查了值有没有发生改变,如果原本值为 A,被改为 B 之后,又被改为了 A,那么 CAS 是不会发现值被改编过了的
ABA 问题解决方案:为每个变量绑定版本号,A–>B–>A 加上版本号为:A1–>B2–>A3

了解 AQS 吗?底层原理是什么?

AQS 是抽象队列同步器,其实就是一个队列,存储的是线程,AQS 的作用就是 去管理线程加锁和解锁时的阻塞、唤醒

AQS 的原理:线程在获取锁失败之后,会被封装成 Node 节点假如到 AQS 阻塞等待,当获取锁的线程释放锁之后,会从 AQS 队列中唤醒一个线程,AQS 队列如下:

线程池的底层工作原理

线程池其实就是对线程做一个 池化 操作,用于线程不断创建、销毁的开销,可以重复利用线程,节省资源

线程池中的重要参数如下:

corePoolSize :核心线程数量
maximumPoolSize :线程池最大线程数量 = 非核心线程数+核心线程数
keepAliveTime :非核心线程存活时间
unit:空闲线程存活时间单位(keepAliveTime单位)
workQueue :工作队列(任务队列),存放等待执行的任务
threadFactory :线程工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。
handler: 拒绝策略 ,如果阻塞队列满了之后,对于新加入的任务该如何处理
除了线程池的核心参数要掌握,任务提交到线程池中的执行流程也要了解:

线程池的参数设置攻略

下边以几种设置的例子,来说明一下会出现的情况:

. 如果将 maximumPoolSize 设置为 Integer.MAX_VALUE
这时,如果瞬间任务很多,核心线程都被占用,那么会无限创建线程去处理任务,导致消耗系统不断消耗资源去创建大量线程,如果任务提交速度大于线程处理速度,系统资源很快就会被耗尽,即使内存没有崩溃,也会导致 CPU 负载很高,所以要避免将 maximumPoolSize 设置的无限大

. 如果在线程中使用无界阻塞队列
如果发生了调用超时,导致队列越来越大,那么会导致任务一直向阻塞队列中存放,内存飙升,甚至出现 OOM 问题

. 自定义拒绝策略
其实可以自己去定义拒绝策略,如果线程池无法处理更多的任务了,可以在自定义的拒绝策略中,将拒绝的任务异步化持久化到磁盘中去,之后再通过一个后台线程去定时扫描这些被拒绝的任务,慢慢执行

如果线上机器突然宕机,线程池的阻塞队列中的请求怎么办?

如果宕机,重启之后,线程池阻塞队列中的任务就会全部丢失

如果想要解决这种情况的话,有这么一个 解决方案:在将任务提交到线程池中去的时候,先把任务在数据库中存储一份,并记录任务执行的状态:未提交、已提交、已完成,执行完之后的话,将任务状态标记为 已完成,如果宕机后,导致任务丢失,就可以去数据库中扫描任务,重新提交给线程池执行

结语

粉丝福利
我是浮生,一个工作十四年经验的Java程序员!

最近很多同学问我有没有java学习资料,我根据我从小白到架构师多年的学习经验整理出来了一份50W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴 可以关注我
公众号:“
灰灰聊架构 ”, 回复暗号:“ 321 ”即可获取

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值