[面试题] ReentrantLock 和 synchronized 有什么区别?为什么它也能支持可重入?
📖 版块 A:知识讲解版 (温故知新)
-
共同点:
- 都是 Java 中的互斥同步手段。
- 都支持可重入,即同一线程可以重复获取同一把锁而不被自己阻塞。
- 都能保证原子性、可见性和有序性。
-
核心区别:
-
实现层级不同:
synchronized是 JVM 层面的内置锁。ReentrantLock是 JDK 层面的显示锁,底层基于 AQS。
-
使用方式不同:
synchronized自动加锁、自动释放。ReentrantLock需要手动lock()/unlock(),通常要配合try/finally。
-
功能特性不同:
ReentrantLock支持 可中断获取锁、超时获取锁、公平锁/非公平锁切换、Condition 条件队列。synchronized在这些扩展能力上较弱。
-
-
可重入实现原理:
ReentrantLock内部维护一个 AQSstate状态值。- 第一次获取成功时,
state从 0 变为 1,并记录持有线程。 - 同一线程再次获取时,直接把
state加 1。 - 释放锁时,
state递减,直到减为 0 才真正释放。
-
适用建议:
- 逻辑简单、同步范围清晰:优先
synchronized。 - 需要更强控制能力:优先
ReentrantLock。
- 逻辑简单、同步范围清晰:优先
🎤 版块 B:面试实战版 (高手逐字稿)
Q:ReentrantLock 和 synchronized 有什么区别?为什么它也能支持可重入?
💬 高手逐字稿:
ReentrantLock 和 synchronized 本质上都是解决多线程互斥访问的问题,但它们属于两套不同层级的实现。
synchronized 是 JVM 原生支持的内置锁,语法层面更简洁,进入同步块自动加锁,退出时自动释放。ReentrantLock 则是 JUC 提供的显示锁,底层基于 AQS,需要我们显式去 lock() 和 unlock()。这意味着它的可控性更强,但同时也更容易因为忘记释放锁而出问题,所以工程上一定要写在 finally 里。
如果从能力上看,ReentrantLock 的优势主要有四个:
第一,它支持公平锁和非公平锁选择;第二,它支持可中断获取锁,也就是线程在等锁时可以响应中断;第三,它支持超时获取锁,比如 tryLock(timeout);第四,它支持多个 Condition 条件队列,能把不同等待条件拆开管理。相比之下,synchronized 更适合简单同步,而复杂并发编排一般会落到 ReentrantLock。
至于它为什么可重入,底层关键就在 AQS 的 state 字段。第一次加锁成功时,state 从 0 改成 1,同时记录当前持有锁的线程是谁。后面如果还是同一个线程再次加锁,AQS 不会阻塞自己,而是直接把 state 再加 1。释放时则对应减 1,只有减到 0 才会真正把锁让出去。所以可重入的本质,就是“记录持有者 + 记录重入次数”。
我在面试里一般还会补一句:现在已经不太建议把它们对立成“ReentrantLock 一定比 synchronized 高级”。Java 6 之后 synchronized 做了很多锁优化,性能差距早就不是核心矛盾了。真正该看的,是你需不需要中断、超时、公平性、多个条件队列这些语义能力。如果不需要,synchronized 更稳、更不容易写错。