继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

为什么你的代码会死锁?这四个条件一旦满足,必挂!

我就是渣哥
关注TA
已关注
手记 106
粉丝 1
获赞 3

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

为什么你的代码会死锁?这四个条件一旦满足,必挂!

最近和朋友撸串,聊到Java多线程的各种坑,果然大家都是“被死锁支配过的灵魂”。有哥们一脸郁闷地说:“我的代码怎么老莫名其妙死锁啊?”我一听,哈哈,这可是我踩过的命里大劫。今天就唠唠这话题——死锁,是怎么死得那么死的。

死锁的四大天王

先说点人话,死锁是啥?就是A线程等B资源,B线程等A资源,大家互相杵着——谁都别想动!

教科书里经典说法:必须满足下面四个条件,死锁才会出现。为了方便记忆,我都给取了个外号:

条件 外号 解释
互斥 “只许一人” 每次只有一个线程能用资源
不可抢占 “老子就不让” 别人用着的资源,谁都不许抢
占有且等待 “我不松手” 先拿着一部分,然后傻傻地还等着别的资源
循环等待 “连环等人” 一个等一个,最后又绕回来等自己(就像汉服群的八卦……)

哎呀, 写着写着就死锁了……

其实刚开始我觉得这种事只会发生在小白身上,直到有天在项目里搞两个锁,结果就在测试环境里挂了大半天。

代码可能长这样(伪代码,别当真小抄——凑个气氛就行):

synchronized(lockA) {
    // ...
    synchronized(lockB) {
        // do something
    }
}
// 另一处
synchronized(lockB) {
    // ...
    synchronized(lockA) {
        // do something else
    }
}

乍一看都没毛病。可惜,我A线程拿A锁等B锁,B线程拿B锁等A锁,两边互不相让。然后你就发现服务像咸鱼一样躺住了,怎么踹都不起来。

踩坑瞬间

有几个经典的“我为死锁狂”时刻,肯定不少人都有感(别谦虚,快认领):

  • 杜撰了点所谓“优雅并发”,结果两个线程互锁住,debug半天发现卡死在同步块里
  • 上生产的时候,一到高并发就疯狂死锁,但本地单步压根儿没复现
  • 以为有了tryLock就安全了,结果忘记tryLock后没拿到锁还傻等
  • 用 synchronized 嵌套太随意,锁顺序乱成锅粥

解决死锁的“土味技巧”

话说到底,如何防死锁?

  • 统一锁顺序
    咋都要锁A和B,就别一个A->B一个B->A。全公司写代码的统一风格,多了少了都不好说。
  • 能用tryLock就用tryLock
    不要强上,拿不到锁就及时放弃,别一死嗑到底。
  • 避免长同步块
    同步块里写少点,别“买锁就包场”,干完赶紧释放资源。
  • 能不用锁就不用锁
    乐观锁、并发容器啥的用起来,不要啥都synchronized。

经验启示

有几点“心头警钟”,写在这里,后面犯错就回来对照一下:

  • 死锁不是魔法,是你亲手造的。四大条件,一条都不少就会出事。
  • 本地OK不等于线上OK,多线程世界充满幻想和意外。
  • 写多线程得像谈恋爱——别贪,别抢,别拧巴。
  • 日志、监控、jstack,大招永远要在手。

说白了,多线程里的死锁,就是“等一个永远也等不到的人”。别再让你的小伙伴互相等死,写代码嘛,咱图个舒心!

——今天就唠到这,耳边又响起leader的声音:“下次写并发,给我规范点!” 收键盘,溜了溜了……

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP