“你知道 Java 里的可重入锁(ReentrantLock)吗?”
听到这个问题的瞬间,我脑子一懵,感觉整个人都不好了。
这可是社招面试啊!面试官可不是在跟你闹着玩,这要是答不上来,我这10K+的大厂 offer 可就凉凉了。
我硬着头皮回忆,嘴里下意识冒出一句:“额……ReentrantLock 是一个可重入的互斥锁,提供了比 synchronized 更灵活的控制方式。”
“能详细说说吗?比如它的实现原理、和 synchronized 的区别、应用场景等等?”
完蛋!面试官竟然“上套”了,一看就是准备把这个问题往深了挖。
今天,我就跟大家好好复盘一下,ReentrantLock 到底是个啥,以及在面试里应该怎么回答才能让面试官眼前一亮!
什么是可重入锁?1. 先聊聊“可重入”这个概念
咱们先不谈 ReentrantLock,单纯想象这么个场景:
你家有个门,锁是你自己装的,钥匙也只有你一个人有。你打开门进了屋,突然发现自己忘了拿手机,于是你又转身开门进去拿。
此时,你仍然在“家”里,对吧?你并不需要重新申请钥匙,因为你已经有了访问权限。
同理,在 Java 里,“可重入”意味着同一个线程在持有某把锁的时候,可以再次获取这把锁,而不会导致自己被阻塞。”
2. synchronized 其实也是可重入的
运行结果:
解析:
methodA() 持有锁,methodB() 也是 synchronized 的,但并不会阻塞自己,而是可以正常执行。
这就是可重入性,如果 synchronized 不是可重入的,那么 methodA() 进入后调用 methodB() 会被自己锁住,导致死锁。
3. ReentrantLock 是个啥?
synchronized 既然已经可重入了,那为什么还要 ReentrantLock 呢?
因为 synchronized 虽然简单易用,但它的功能有限,比如:
无法尝试获取锁(想要获取锁但不阻塞等待)
无法中断等待(线程等待锁时无法响应中断)
无法实现超时获取锁(避免无限等待)
无法公平加锁(锁的获取没有 FIFO 规则)
于是,JDK 提供了 java.util.concurrent.locks.ReentrantLock,它是 synchronized 的增强版,提供了更灵活的锁机制。
ReentrantLock 的核心功能1. 基本使用
输出(执行顺序可能不同):
解析:
lock.lock() 获取锁,如果锁已经被占用,会等待。
lock.unlock() 释放锁,防止死锁。
finally 确保锁一定会被释放(必须养成好习惯)。
2. 可重入性
ReentrantLock 当然也是可重入的,否则它就不配叫 ReentrantLock 了:
一个线程持有锁时,可以再次获取该锁,而不会被阻塞!
每 lock() 一次,就需要 unlock() 一次,否则锁不会释放。
ReentrantLock vs synchronized面试官一般会问:
什么是可重入锁?
synchronized 是可重入的吗?
ReentrantLock 和 synchronized 的区别?
ReentrantLock 的应用场景?
标准回答:
可重入锁指的是同一个线程在获取锁后,可以再次获取同一把锁,而不会被阻塞。synchronized 和 ReentrantLock 都是可重入的,但 ReentrantLock 相比 synchronized 提供了更多的特性,比如支持公平锁、可中断锁、超时获取锁等。ReentrantLock 适用于高并发场景,比如数据库连接池、任务调度等,而 synchronized 适合简单的同步需求。
加分项:
ReentrantLock 是基于 AQS(AbstractQueuedSynchronizer)实现的,通过 CLH 队列来管理锁的获取顺序,并提供可中断锁、超时等待锁等特性,使得线程调度更高效。
总结可重入锁:同一个线程可以多次获取同一把锁。
ReentrantLock 是 synchronized 的增强版,提供更灵活的锁机制。
使用场景:
synchronized 适合简单同步,代码简洁。
ReentrantLock 适用于高并发场景,比如数据库、任务调度等。
面试要点:
说清可重入的概念。
结合 synchronized 进行对比。
提及 ReentrantLock 的特性和应用场景。
END如果你在面试中遇到这个问题,现在你已经胸有成竹了!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!