为了账号安全,请及时绑定邮箱和手机立即绑定

面试官狂追问:你能说清楚读写锁的实现原理吗?

标签:
Java JavaScript

原文来自于:https://zha-ge.cn/java/71

面试官狂追问:你能说清楚读写锁的实现原理吗?


还记得那次面试,面试官一脸“你懂个啥”的表情,翻开了终极杀器——“说说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确实能救命。但每次用,都得带着“别把自己锁死”的敬畏之心。

好了,瓜子磕完。各位看官,锁好门,别忘了解锁,且用且珍惜~

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消