ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

第六章 死锁(并发经典BUG,面试必背)

第六章 死锁(并发经典BUG,面试必背) 定位死锁是多线程编程中严重的并发BUG会导致线程永久阻塞、程序卡死无法终止是并发面试的高频核心题。1. 什么是死锁定义多个线程同时被阻塞它们都在等待对方持有的资源被释放而双方都不释放自己占有的资源最终所有线程无限期等待程序无法正常结束。通俗例子滑稽老哥和女神一起吃饺子吃饺子需要酱油、醋两瓶调料。滑稽拿起了酱油瓶女神拿起了醋瓶滑稽你先把醋给我我用完就把酱油给你女神你先把酱油给我我用完就把醋给你两人互不相让都等着对方先释放资源就形成了死锁。对应关系酱油、醋是两把锁滑稽、女神是两个线程。经典模型哲学家就餐问题场景5个哲学家围坐圆桌中间一盘面条每两人之间放一根筷子共5根。 哲学家只有两个动作思考人生放下筷子、吃面条拿起左手边右手边两根筷子。死锁触发同一时刻5个哲学家同时拿起了自己左手边的筷子。 此时每个人都只有一根筷子都想拿右手边的筷子但右手边的筷子都被旁边的人拿着。所有人都不放下自己的筷子也拿不到第二根全部阻塞等待形成死锁。2. 死锁产生的四个必要条件面试必背⚠️必须四个条件同时满足才会产生死锁破坏其中任意一个就能解除死锁。互斥使用资源被一个线程占有后其他线程不能同时使用。这是锁的基本特性。不可抢占资源只能由持有者主动释放其他线程不能强行夺走。synchronized、ReentrantLock都符合这个特性。请求且保持线程已经占有了一个资源同时又去申请新的资源并且不释放已经占有的资源。循环等待线程之间形成环形等待链线程1等线程2的资源线程2等线程3的资源线程3等线程1的资源形成闭环。3. 如何避免死锁核心思路四个必要条件中破坏任意一个就能避免死锁。 工程上最常用、成本最低的方案是破坏循环等待条件。最常用方案锁排序方法给所有的锁统一编号所有线程必须按照编号从小到大的固定顺序获取锁。 所有线程获取锁的顺序完全一致不会出现反向等待的环路从根源消除循环等待。代码对比❌ 会死锁的代码锁顺序相反形成循环等待Object lock1 new Object(); Object lock2 new Object(); // 线程1先拿lock1再拿lock2 Thread t1 new Thread(() - { synchronized (lock1) { synchronized (lock2) { // 业务逻辑 } } }); // 线程2先拿lock2再拿lock1顺序相反形成循环等待 Thread t2 new Thread(() - { synchronized (lock2) { synchronized (lock1) { // 业务逻辑 } } });✅ 修复后的代码统一锁顺序破坏循环等待两个线程都遵循先获取lock1再获取lock2。Object lock1 new Object(); Object lock2 new Object(); Thread t1 new Thread(() - { synchronized (lock1) { synchronized (lock2) { // 业务逻辑 } } }); Thread t2 new Thread(() - { // 统一顺序先lock1再lock2 synchronized (lock1) { synchronized (lock2) { // 业务逻辑 } } });原理所有线程获取锁的顺序完全一致不会出现你等我、我等你的环路破坏了循环等待条件不会产生死锁。4. 面试标准答法问谈谈什么是死锁如何避免死锁 答死锁是多个线程互相持有对方需要的资源都不释放自身资源导致所有线程永久阻塞的并发BUG。 死锁产生有四个必要条件互斥使用、不可抢占、请求且保持、循环等待。 避免死锁可以通过破坏四个必要条件实现工程上最常用的是锁排序给所有锁统一编号所有线程按固定顺序获取锁破坏循环等待条件从根源避免死锁。
返回列表