孤舟笔记 并发篇八 可重入锁是什么?为什么面试官说没有它synchronized就是个残废
文章目录先说结论可重入锁的核心要点没有可重入锁会怎样一个自我死锁的灾难可重入锁是怎么实现的计数器 线程判断synchronized 的可重入JVM 层面天然支持可重入锁的注意事项可重入锁全景回答技巧与点评标准回答加分回答面试官点评个人网站你有没有写过这样的代码——方法 A 加了synchronized方法 B 也加了synchronized然后 A 调用了 B。按理说A 已经拿到锁了再去拿同一把锁会不会把自己锁死如果锁不可重入A 拿着锁等自己释放锁——这就是死锁。但神奇的是程序跑得好好的并没有卡住。这就是可重入锁在默默保护你。先说结论可重入锁的核心要点维度说明什么是可重入同一线程可以重复获取同一把锁不会死锁实现原理持锁线程 ID 判断 计数器state每次加锁state 1每次解锁state -1完全释放state 减到 0 才真正释放锁synchronized天然可重入JVM 实现ReentrantLock可重入AQS 的 state 计数不可重入的后果同一线程重入时自己锁死自己一句话记住可重入锁就像你家门锁——你在屋里再推内门不用重新开锁不可重入就是你把自己锁在卧室了。没有可重入锁会怎样一个自我死锁的灾难假设锁不可重入看这段代码publicsynchronizedvoidmethodA(){methodB();// A 已持有 this 锁调用 B 还要拿 this 锁 }publicsynchronizedvoidmethodB(){// 如果锁不可重入这里会永远等待 → 自己死锁}方法 A 拿到this对象的锁调用方法 B方法 B 也要拿this的锁。如果锁不可重入线程 A 会等自己释放锁——左手等右手永远等不到。生活类比你进了家门卧室门也要用同一把钥匙。如果门锁不可重入你进了家门再想进卧室——对不起钥匙被你自己拿着呢你得先释放家门锁才能开卧室门。但你人在家里家门锁怎么释放死锁了。可重入锁就是解决这个问题你已经拿过这把锁了再拿一次直接放行不用等。可重入锁是怎么实现的计数器 线程判断可重入锁的核心就两步判断当前线程是不是已经持有这把锁→ 是的话直接放行计数器 1表示又重入了一次以ReentrantLock为例它基于 AQS 实现// AQS 内部简化finalbooleannonfairTryAcquire(intacquires){intcgetState();// 获取当前 stateif(c0){// 锁空闲CAS 抢锁 if(compareAndSetState(0,acquires))setExclusiveOwnerThread(Thread.currentThread());}elseif(getExclusiveOwnerThread()Thread.currentThread()){// 同一线程重入 state 累加setState(cacquires);returntrue;}returnfalse;}关键逻辑判断当前线程是否是持锁线程。如果是state 1直接放行。释放锁时反过来protectedfinalbooleantryRelease(intreleases){intcgetState()-releases;// state - 1 if(c0){setExclusiveOwnerThread(null);// state 为 0 才真正释放 returntrue;}setState(c);returnfalse;}state 减到 0 才真正释放锁。加了 3 次锁就得解锁 3 次。synchronized 的可重入JVM 层面天然支持synchronized的可重入不需要你操心JVM 在底层就搞定了。每个对象头里有个 Monitor内部有计数器_count逻辑和ReentrantLock一模一样同一线程再进synchronized→ 计数器 1退出synchronized→ 计数器 -1计数器归零 → 释放 Monitor所以你写synchronized嵌套调用从来不用担心自己死锁自己——JVM 帮你兜底。可重入锁的注意事项可重入锁虽然好用但有几个坑1. 加几次锁就得解几次锁lock.lock();lock.lock();// state 2lock.unlock();// state 1锁还没释放// 必须再 unlock() 一次state 才归零lock.unlock();// state 0锁真正释放2. 子类重入父类的 synchronized 方法classParent{publicsynchronizedvoiddoSomething(){/* ... */}}classChildextendsParent{OverridepublicsynchronizedvoiddoSomething(){super.doSomething();// 重入同一把锁this}}子类调用super.doSomething()是重入不会死锁。但如果子类和父类用不同的锁对象那就不是重入了。可重入锁全景可重入锁 全景 核心机制 ├── 线程判断 ── 当前线程 持锁线程 → 放行 ├── 计数器 ── state 记录重入次数 │ ├── 加锁 state 1 │ └── 解锁 state -1归零才释放 └── 两个实现 ├── synchronized ── JVM Monitor 计数天然可重入 └── ReentrantLock ── AQS state 计数显式可重入 注意事项 ├── 加几次锁解几次锁 ├── 子类调父类 synchronized 是重入 └── 不同锁对象不算重入 口诀同线程可重入计数器来帮忙 加几次解几次归零才是释放。回答技巧与点评标准回答可重入锁是指同一线程可以重复获取同一把锁而不会死锁。实现原理是通过线程 ID 判断和计数器持锁线程再次获取锁时计数器加 1 而非阻塞释放锁时计数器减 1减到 0 才真正释放。synchronized 天然可重入JVM Monitor 计数ReentrantLock 也支持可重入AQS 的 state 计数。可重入锁的作用是避免同一线程在嵌套调用时自我死锁。加分回答设计原则可重入锁体现了最小意外原则——如果同一线程已经持有锁再次获取应该是自然行为而非死锁。几乎所有主流锁实现都支持可重入不可重入锁属于特殊场景边界情况ReentrantLock的lock()和unlock()必须成对出现嵌套几次就 unlock 几次。如果只 unlock 一次锁不会释放其他线程永远等待——这是synchronized不会出的问题实际应用框架中大量使用可重入特性。比如 Spring 的事务管理器在Transactional嵌套调用时同一线程可以重复获取数据库连接锁AQS 的acquire()方法本身也是可重入设计面试官点评这道题考的是你对锁机制的底层理解。只说可重入就是能重复加锁太浅了。能讲清楚 state 计数器的实现、为什么必须减到零才释放、以及 synchronized 和 ReentrantLock 在可重入上的不同实现路径才能拿高分。如果你能指出可重入是锁的基本能力不是高级特性面试官会认为你理解到位。原文阅读内容有帮助点赞、收藏、关注三连评论区等你
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2571485.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!