Sunday面试指南

死锁产生的四个必要条件是什么?预防、避免和检测有什么区别?

下面是一段教学用的模拟面试。

🧑‍💻 面试官: 死锁有哪些必要条件?

🙋‍♂️ 我: 互斥、占有且等待、不可抢占、循环等待。

🧑‍💻 面试官: A 拿着锁一等锁二,B 拿着锁二等锁一,你准备破坏哪一个条件?

🙋‍♂️ 我: 让大家统一按锁一、锁二的顺序获取,可以避免循环。

🧑‍💻 面试官: 如果只是超时后立刻重试,两个线程总一起退、一起抢,又是什么问题?

四个条件不是背诵终点。要能指出资源等待环,并选择确实能破坏它的策略。

面试速答(60 秒版)

经典资源死锁通常需要同时具备四个条件:资源互斥、持有部分资源并等待其他资源、资源不能被强制抢占,以及存在循环等待。

预防死锁可以针对其中一个条件设计。比如统一锁顺序破坏循环等待,避免持锁等待其他资源,或者在允许的情况下设计可撤销、可释放的获取过程。

但死锁、饥饿和活锁不同。没有死锁,不代表某个任务不会一直拿不到资源;不断退让和重试,也可能忙着动作却没有实际进展。

工程里先画等待关系,再检查获取顺序、持锁范围和失败清理。超时帮助退出等待,但不能代替完整的恢复与重试设计。

死锁需要闭合等待环

图:死锁需要闭合等待环。

知识点详解:为什么两个线程都在等,却谁也走不了?

把两把锁画出来

假设线程 A 已拿锁一,继续等锁二;线程 B 已拿锁二,继续等锁一。两把锁都只能由一个线程持有,也不会自动从持有者手里被抢走。

A 只有拿到锁二才肯继续,B 只有拿到锁一才肯继续。于是我们能画出一个闭合的等待环。

这四个条件在这里都有对应动作。相比只背名称,把“谁持有谁、谁在等谁”说清楚,更容易解释原因。经典分析见 OSTEP 并发缺陷章节。

统一顺序为什么有效?

如果所有线程都先拿锁一,再拿锁二,那么拿不到锁一的线程还没有持有锁二。

这样就不会形成“各自持有一把、互等另一把”的这个环。注意是所有参与者都遵守同一顺序,不是只给当前方法改一下。

多层函数里隐含的加锁顺序也要检查。表面上主函数按顺序,调用的内部方法却再拿另一把锁,同样可能破坏约定。

统一顺序,破坏循环等待

图:统一顺序,破坏循环等待。

一次性申请全部资源,总是更好吗?

如果能在开始时取得全部所需资源,可以减少持有部分资源继续等待的情况。但需求可能提前不确定,长期占用暂时不用的资源,也会降低并发。

同样,缩小持锁范围通常有帮助,但不能拆掉保护不变量所需的整体操作。避免死锁和保持数据正确性,要一起满足。

锁、文件句柄和连接这类资源的退出路径也重要。异常发生后没释放,不一定形成同样的环,却可能使后来所有任务长期等待。

超时重试,为什么还要讲策略?

超时能让某些等待退出,但超时后已经持有的资源是否释放、部分操作能否撤销、重试是否有副作用,都需要定义。

两个任务如果每次同步退让、同步重试,可能不断改变状态却谁也完不成。这更接近活锁。加入合理退避、随机化或重新安排竞争,只是可能的工具,还要针对具体协议设计。

饥饿则是某个任务持续拿不到机会,其他任务仍在前进。三者不能因为“请求一直不完成”就统一叫死锁。

怎样验证不会只靠运气?

列出多资源获取路径,检查全局顺序和异常清理;测试里主动安排交错,让两个任务分别拿到第一份资源再继续。

线程栈和等待图可以帮助定位,实际工具取决于运行时。本文是跨语言机制解释,没有把示意图当作真实线程转储。

面试官继续追问

发现环就一定是所有资源模型下的死锁吗?

要看资源模型。单实例资源的等待环分析比较直接;多实例等情况,还需要更完整的判断,不能无限推广简单图。

没有死锁就线程安全吗?

不是。数据竞争、原子性错误、活锁和饥饿仍可能存在,各自需要相应检查。

面试速记卡

  • 条件:互斥、占有且等待、不可抢占、循环等待。
  • 排查:先标持有关系与等待关系。
  • 顺序:所有参与者遵守统一资源获取顺序。
  • 超时:要配合释放、回退和重试策略。
  • 区分:死锁不前进,活锁忙而无进展,饥饿缺少机会。

公司面试真题

真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。

  • 阿里巴巴 · 后端 · 原帖未明确批次

    死锁怎样产生,怎样避免?(题意整理)

    阿里云后端面经 ↗
    原帖发布于 2022-03-29

  • 小米 · 前端 · 原帖未明确批次

    死锁怎样产生,怎样避免?(题意整理)

    小米前端二面 ↗
    原帖编辑于 2024-05-29

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历