- 公务员面试不强制穿正装,但穿了真的能加分(2-8)
- 羽绒服面试公务员,不是段子,是现实。 那日,哈尔滨的气温达零下五度,考场外,队伍排列着,恰似螃蟹
若处于Java开发岗位的面试情境当中,会发现并发编程乃是无法避开的核心考查模块。那“乐观锁与悲观锁”作为并发控制的基础机制,更是频繁出现的考点啦——依据近期100多家互联网公司的面试真题统计,可知该知识点的考查频率高达68%。并且接近半数的面试官会扩展追问底层的实现方式,、场景的选择类型以及避免踩坑的技巧。有不少候选人由于仅仅掌握了表面的定义,在面对延伸性提问的时候逻辑变得混乱,从而错失了获得offer的机会。就在今天,会从专业的视角带领大家全面地拆解这一考点,涵盖原理、实战以及答题的技巧,以此帮助大家在面试的时候能够从容地应对。
面试中乐观锁与悲观锁的考察核心
首先,要明确核心结论,面试官对乐观锁与悲观锁进行考察,其本质在于检验候选人对于“并发场景下控制数据一致性”的理解深度,以及能否在结合业务场景的情况下做出合理的技术选型。从面试题设计这个角度来看,考察维度主要被分为三层。
1. 基础层面:是否能够清楚地分辨出两种锁的核心定义以及设计思想,这乃是入门的门槛所在;2. 原理层面:是否能够讲明白两种锁在Java当中的底层实现情况(像是悲观锁的synchronized、乐观锁的CAS),这是能力区分的要点;3. 应用层面:是否能够结合具体的业务场景(诸如高并发库存扣减、低并发用户信息更新)来阐述选型逻辑,这是面试官重点关注的实战能力体现。
相当多的候选人仅仅滞留在基础层面,对于原理以及应用层面的领会含混不清,进而致使答题欠缺完整性。接下来,我们自原理层面着手进行深度剖析。
两种锁的核心逻辑与Java实现
为了弄明白两种锁,首先得抓住核心差别:对于并发场景的“信任程度”存在不同,而这直接决定了它们的实现逻辑,也决定了适用场景。
1. 悲观锁:“不信任”并发,先上锁再操作
核心思想是,觉得在并发的景象之下必然会出现数据竞争的情况,像是多个线程一块儿去修改同一个数据这般,所以在对数据展开操作之前,一定要先去获取锁,以此来禁止其他的线程进行操作,一直到自身操作完毕之后方才释放锁。这样的一种思路跟 “先占坑再办事” 相类似,借助强制的排他性以此来确保数据的一致性。
Java中的典型实现:
悲观锁具备突出的优缺点:其优点在于达成方式简易可行,可做到对数据一致性的百分百保障;而其缺点则是锁竞争会致使线程处于阻塞状态,此情况在高并发的场景当中会造成系统吞吐量的下降局面,并且还存在出现死锁潜藏风险。
2. 乐观锁:“信任”并发,先操作再校验
核心思想是,觉得在并发的场景当中,数据竞争的概率比较低,所以在进行操作数据之前,不会去上锁,而是直接开展修改操作,在最终提交数据的时候,借助校验机制来判定数据是不是被其他的线程修改过,要是没有被修改,那么提交就成功,要是被修改了,那就放弃操作并且重新尝试,或者返回失败结果。这种思路跟“先把事情办了再去检查”相类似,依靠无锁设计来提高并发性能。
Java中的典型实现:
乐观锁具备相关优缺点,其优点在于不存在锁竞争情况,处在高并发场景时吞吐量较高,并且不会出现死锁现象,缺点是存在所谓“ABA问题”,也就是数据先是被进行修改,之后又被改回到原来的值,进而致使校验出现误判,而且重试机制会消耗CPU资源,它适合那种并发冲突较少的场景。
两种锁的代码实现与场景适配
唯有将理论与实战相结合,方可切实掌握。接下来,以“电商库存扣减”此一经典并发场景作为示例,分别达成悲观锁以及乐观锁,并且剖析各自适用的场景。
1. 悲观锁实现:基于synchronized

场景适配方面,库存扣减属高并发场合,但要是业务规定“绝对不可以超卖”,并且并发量并非极端高,像每秒几百次请求这种情况,能够采用悲观锁确保数据一致性。
// 库存服务类
public class StockService {
// 模拟库存数量
private int stock = 100;
// 悲观锁实现:synchronized修饰方法,保证原子性
public synchronized boolean deductStock(int num) {
System.out.println(Thread.currentThread().getName() + "开始扣减库存,当前库存:" + stock);
if (stock >= num) {
// 模拟业务处理耗时
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock -= num;
System.out.println(Thread.currentThread().getName() + "扣减成功,剩余库存:" + stock);
return true;
} else {
System.out.println(Thread.currentThread().getName() + "扣减失败,库存不足");
return false;
}
}
// 测试方法
public static void main(String[] args) {
StockService stockService = new StockService();
// 模拟10个线程同时扣减库存
for (int i = 0; i < 10; i++) {
new Thread(() -> {
stockService.deductStock(10);
}, "线程" + (i + 1)).start();
}
}
}
代码说明:借助于synchronized对deductStock方法进行修饰,以此确保同一时刻仅有一个线程能够去执行这个方法,从而完完全全地规避了因并发修改而引发的超卖现象哦。然而其不足之处则是线程会进行排队来执行,在那种每秒有着几千次请求的极为极端的高并发情形之下,就会出现响应延迟这样的情况呢。
2. 乐观锁实现:基于CAS(AtomicInteger)
若是库存扣减的并发量特别高,高到每秒会有几千次请求涌现出来,并且允许存在少量次数的重试情况,而冲突概率又比较低,这种场景适配下,能够运用乐观锁以便提升吞吐量。
import java.util.concurrent.atomic.AtomicInteger;
// 库存服务类
public class StockServiceCAS {
// 基于AtomicInteger的CAS实现乐观锁
private AtomicInteger stock = new AtomicInteger(100);
// 乐观锁实现:CAS无锁扣减库存
public boolean deductStock(int num) {
while (true) {
int currentStock = stock.get();
System.out.println(Thread.currentThread().getName() + "开始扣减库存,当前库存:" + currentStock);
if (currentStock < num) {
System.out.println(Thread.currentThread().getName() + "扣减失败,库存不足");
return false;
}
// CAS操作:比较并交换,若当前库存等于预期值,则更新为新值
if (stock.compareAndSet(currentStock, currentStock - num)) {
System.out.println(Thread.currentThread().getName() + "扣减成功,剩余库存:" + (currentStock - num));
return true;
} else {
System.out.println(Thread.currentThread().getName() + "扣减冲突,重试...");
}
}
}
// 测试方法
public static void main(String[] args) {
StockServiceCAS stockService = new StockServiceCAS();
// 模拟20个线程同时扣减库存
for (int i = 0; i < 20; i++) {
new Thread(() -> {
stockService.deductStock(5);
}, "线程" + (i + 1)).start();
}
}
}
代码说明:运用AtomicInteger的get()方法去获取当下库存,借由compareAndSet()方法达成CAS操作。要是在这期间存在别的线程对库存进行了修改,那么CAS操作将会失败,借助while循环来重新尝试,直至成功或者判定库存不足。采用这种方式是不需要上锁的,线程能够并行执行,并且在高并发状况下吞吐量会更高,不过要留意控制重试次数,防止在极端冲突情形下CPU出现空转。
3. 场景选型总结
面试中被问到选型时,可直接套用这个逻辑:
面试答题模板与避坑要点
经由结合数量众多的面试案例,对“乐观锁与悲观锁”的答题模板予以整理,只要直接进行套用,便能够覆盖百分之九十的面试提问,其中。
1. 标准答题模板
首先进行第一步:所谓定义区分,即悲观锁秉持并发必然会产生冲突的看法,会先行上锁而后才开展操作;而乐观锁却是觉得并发时冲突情况较少,会先予以操作进而再作校验。接着步入第二步:关于原理对比,悲观锁的核心要点在于排他性锁。
(synchronized/ReentrantLock),乐观锁的核心在于CAS/版本号。第三步:对优缺点展开分析 ,悲观锁具备简单可靠的属性 ,然而并发性能较差 ,乐观锁并发性能高 ,但存在ABA这类问题。第四步:进行场景选型 ,依据冲突概率 、一致性要求 、并发量来做出选择 (参照上文的场景总结)。
2. 高频避坑要点总结
乐观锁跟悲观锁的关键在于“对并发情形的信任差别”,它们不存在绝对的好坏之分,重点是适配业务情形 ,面试考查的关键并非记住定义,而是领会原理、把握选型逻辑 ,记住“定义 - 原理 - 优缺点 - 选型”的回答框架 ,接着掌握ABA问题、锁升级等细节 ,便能够轻松应对这类询问。
最后,提议结合本文实战代码,亲自动手测试一回,领会不同场景下两种锁的执行成效。要是在面试里碰到更复杂的延伸问题(像“CAS的底层实现”“synchronized与ReentrantLock的性能对比”),能够在评论区留言,我会逐个拆解。
将本文进行收藏,在面试之前再次去把一遍答题的模板以及避坑的要点再过一下,祝愿你能够顺利地拿到自己心里所喜欢的职位录用通知!
温馨提示:本内容地址http://m.ysjob.cc/article/articledetail-389483.html转载请注明,以上Java开发面试高频考点:乐观锁与悲观锁原理及答题技巧资讯信息来自颍上人才网(颍上地区最大的颍上人才网,颍上人才网)