图 1:本文封面,点明题目与「对比加选型」的主线。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料(见文末来源说明),与任何公司的当前招聘无关。「synchronized 和 Lock 的区别」是 Java 并发面试的保留曲目,人人都能背出两三条,面试官真正想看的是你能不能把差异组织成一张有结构的对比表,并落到「什么场景用哪个」的决策上。
想象面试现场,面试官问:「synchronized 和 ReentrantLock 都用过吧?说说区别,再说说你的项目里怎么选。」
第一步,审题。
把这题拆成两层:差异在哪、怎么选型。差异不要零散地背,按六个维度对照着讲——出身、用法、中断、超时、公平、条件队列;选型则是一句决策口诀加一个前提:默认用 synchronized,需要 Lock 的独门能力时再换。
第二步,六个维度对照。
出身。synchronized 是 JVM 内置关键字,锁的是每个对象自带的监视器;Lock 是 JDK 的接口(代表实现 ReentrantLock),官方文档开篇即点明定位:Lock 实现提供比 synchronized 方法和语句更广泛的锁操作。一个是语言特性,一个是普通 API,这决定了后面所有差异。
用法。synchronized 进代码块自动加锁、出代码块(包括异常)自动释放;Lock 没有这种块结构保护,官方文档给出必须遵守的惯用法——lock() 之后紧跟 try,业务代码放 try 里,unlock() 放 finally 里。自动释放换来的是省心,手动释放换来的是灵活:锁可以在不同作用域、按任意顺序获取和释放(比如链式遍历时的 hand-over-hand 锁定),这是 synchronized 结构上做不到的。
中断与超时。线程等 synchronized 的锁,拿不到就一直等,不可中断;Lock 提供 lockInterruptibly()(等待时可响应中断)、tryLock()(拿不到立刻返回 false)和 tryLock(long, TimeUnit)(限时等待,超时返回 false)。死锁排查和服务降级里,这三样东西是救命的。
公平。synchronized 是非公平的,没的选;ReentrantLock 的构造器接受可选的公平参数——官方文档说明,公平锁在竞争下倾向把锁授予等待最久的线程,能杜绝线程饿死,代价是整体吞吐量明显更低。注意一个文档里写明的例外:无参 tryLock() 不讲公平,锁空着就抢(barging),要守规矩请用 tryLock(0, TimeUnit.SECONDS)。
条件队列。synchronized 配 wait/notify,一个对象只有一个等待队列,notify 唤醒谁都由不得你;Lock 可以挂多个 Condition,官方文档说明 newCondition() 返回绑定在该锁上的条件实例,等待与唤醒可以分队列进行——生产者消费者里「非空」「非满」两个条件分开唤醒,就靠它。
第三步,一个容易漏的共同点。
讲完全部差异,要主动补一句:官方文档要求所有 Lock 实现必须提供与内置监视器锁完全相同的内存同步语义——成功 lock 与 unlock 的内存效果和监视器的 Lock/Unlock 动作一致。也就是说,两者的可见性保证相同,差异只在功能与灵活性,不在「哪个更正确」。
图 2:六维对比表与选型决策条。
第四步,选型决策。
口诀:默认 synchronized。写法简单、不会漏 unlock、JVM 对它持续优化,绝大多数同步场景它就是最优解。什么时候换 Lock:需要超时等待(拿锁失败要降级)、需要可中断(任务取消语义)、需要公平锁(严格 FIFO 防饿死)、需要多个条件队列(精细的等待唤醒)——四个「需要」出现任何一个,就是 ReentrantLock 的主场。反之,仅仅因为「Lock 听起来更高级」而换,是把 finally 漏 unlock 的风险引进门。
易错点清单。
一,用 Lock 却不写 try/finally——异常一路抛出去,锁永远不还,这是 Lock 最经典的线上事故。二,以为公平锁又快又公平——官方文档明说它通常明显更慢,公平是拿吞吐量换的。三,以为 synchronized 性能一定差——JVM 对它有锁消除、锁升级等一系列优化,「synchronized 慢」是过时结论。四,背六条差异却答不出选型——面试官的落点永远是「你的场景用哪个、为什么」。
回到面试现场,建议的答题节奏:先给选型口诀(默认 synchronized,四个需要才上 Lock),再按六维度展开对比,然后补内存语义相同这个共同点,最后举自己项目里一个真实的用锁场景收尾。有结构、有取舍、有实例,这题就答满了。
推理服务与数据管道里的连接池、缓冲队列都涉及锁选型。如果面试官追问「读多写少场景下,比 ReentrantLock 更合适的锁是什么」,你能答上来并说出代价吗?欢迎写出你的答案。
参考资料
- Lock(JDK 21 官方文档:比 synchronized 更广泛的锁操作、try-finally 惯用法、tryLock/lockInterruptibly/限时 tryLock、多 Condition、与内置监视器锁相同的内存同步语义) — https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/Lock.html
- ReentrantLock(JDK 21 官方文档:与 synchronized 相同的基本行为与语义、可选公平参数及吞吐量代价、无参 tryLock 的 barging 行为、hold count 上限与监控方法) — https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html