面試官狂追問(wèn):你能說(shuō)清楚讀寫鎖的實(shí)現(xiàn)原理嗎?
面试官狂追问:你能说清楚读写锁的实现原理吗?
还记得那次面试,面试官一脸“你懂个啥”的表情,翻开了终极杀器——“说说Java读写锁的实现原理?”
那语气就好像我上了错车,还把瓜子撒在他座位上。
于是故事就从这颗“读写锁”的瓜子说起来吧。
当我还是个小天真的synchronized信徒
刚入行那会儿,锁对我来说就两个字:synchronized。
反正抢到了就进门,抢不到就等,完事儿。
结果很快就遇到需求:多个线程经常读,但偶尔写。
一群线程进门就是“我要看看”,突然有个小哥儿冲进来说“我要改!”——结果大家全都堵门口。
这下可好,为了一点点写操作,全世界都停下来看他改。我那点性能优化的梦想碎一地。
读写锁初印象:谁说饭不能分开吃?
这时有人指给我看了ReadWriteLock。
“诶,这玩意能让大家一起吃饭(读),只有要上菜(写)时才叫停。”
我一听,这不就是食堂嘛——饭菜还分批上的!
用代码大概长这样:
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 读的时候
lock.readLock().lock();
try {
// 读操作
} finally {
lock.readLock().unlock();
}
写锁用writeLock()即可,写操作的时候大家就老实坐那等。
踩坑瞬间
然而故事怎么可能就这么简单结束呢?
我调皮了一下,遇到了下面的坑:
- 还没写完,有人进来读,居然被卡着,死活等不到。
- 连续多个线程都写,外面一堆读请求排队等疯了。
- 忘记
unlock(),锁永远不释放,把自己坑死。
说出来都是泪,记得有一回,我把unlock()写外面了,结果可想而知——线程全部在外面冻成狗,还被组长怼了一顿。
那背后的玄机到底是哪样?
说句实话,刚开始我真没搞懂ReentrantReadWriteLock有啥魔法。
后来扒源码,发现它维护了两个“队伍”——读线程和写线程。写锁就是“排他模式”,没人能进。读锁则是“共享模式”,一群人同时读也不怕被踩脚。
最有意思的是它怎么确保写的时候没人能进读队:靠CAS——原子性地加减计数。
简单粗暴,但有效!
核心实现(大致意思):
// 非完全源码展示
if (isWriteLockHeld() || hasQueuedWriter()) {
// 写锁被占用,或者有写线程等待时,读锁进不来
return false;
}
readLockCount.incrementAndGet();
return true;
而且这个锁还能“可重入”,就是同一个线程可以多次加锁不炸。
不过,别以为一把锁就能统治江湖。搞不好还会出现“写饥饿”——成天没人让写的机会,因为读的实在太多。
经验启示
后来,我总结了几个血泪教训,列个表吧,别再走我老路:
| 坑点 | 真实体验 |
|---|---|
| 忘记解锁 | 线程僵死,等到天荒地老 |
| 写锁被饿死 | 读一直抢,写永远没机会 |
| 滥用读写锁 | 并发少时,反而性能变慢 |
| 锁粒度太大 | 系统变全局单线程,快不起来 |
| 用错场景 | 低并发+频繁写,根本用不到它 |
收个尾巴(面完还要吃碗面)
再回到面试现场,我把上面这些坎都和面试官“家常”了一遍。
对面先是愣了愣,最后笑着点了点头。虽然不知他心里怎么想,反正,我觉得起码比背书更有趣吧。
以后遇到读多写少的场景,ReadWriteLock确实能救命。但每次用,都得带着“别把自己锁死”的敬畏之心。
好了,瓜子磕完。各位看官,锁好门,别忘了解锁,且用且珍惜~
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章