并发王者课-黄金1:两败俱伤-互不相让的线程如何导致了死锁僵局

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"欢迎来到《","attrs":{}},{"type":"link","attrs":{"href":"https://juejin.cn/post/6967277362455150628","title":"","type":null},"content":[{"type":"text","text":"并发王者课","attrs":{}}]},{"type":"text","text":"》,本文是该系列文章中的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第11篇","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本篇文章中,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"我将为你介绍多线程中的经典问题-死锁,以及死锁的产生原因、处理和方式预防措施","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、死锁的产生","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"观察下面这幅图,线程1持有了A,但它需要B;而线程2持有了B,但是它需要A。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你看,问题就来了,A、B都在等待对方已经持有的资源,并且都不释放,这就让事情陷入了僵局,也就是产生了","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"死锁","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1a729deb6852ca5e8099543265a52e16.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在并发编程中,死锁表示的是一种状态。在这种状态下,各方都在等待另一方释放所持有的资源,但是它们之间又缺乏必要的通信机制,导致彼此存在环路依赖而永远地等待下去。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"死锁不仅存在于Java程序中,在诸如数据库等其他中间件及分布式架构中都会存在。在数据的设计中,会考虑到死锁的监测和恢复。当数据库中发生死锁时,将选择一个牺牲者并放弃对应的事务,同时释放锁定的资源。在它的竞争者执行结束后,应用程序可以重新运行这个事务,因为它的竞争者此前已经完成事务。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,在JVM中,处理死锁并没有数据库中那么优雅。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"当一组线程发生死锁时,“游戏”将到此结束,这些线程将不能再使用,而这可能会直接导致应用程序崩溃、性能降低或者部分功能停止","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,和其他并发问题一样,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"死锁是危险的,死锁造成的影响会立即表现出来,而如果在高负载情况下,这将是一场灾难","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、死锁产生的必要条件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从第一小节的图示中,我们可以看到死锁产生的一些必要条件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"互斥","attrs":{}},{"type":"text","text":":一个资源每次只能被一个线程使用。比如,上图中的A和B同时只能被线程1和线程2其中一个使用;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"请求与保持条件","attrs":{}},{"type":"text","text":":一个线程在请求其他资源被阻塞时,对已经持有的资源保持不释放。比如,上图中的线程1在请求B时,并不会释放A;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不剥夺条件","attrs":{}},{"type":"text","text":":对于线程已经获得的资源,在它主动释放前,不可以主动剥夺。比如,上图中线程1和线程2已经获得的资源,除非自己释放,否则不可以被强制剥夺;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"循环等待条件","attrs":{}},{"type":"text","text":":多个线程之间形成环状等待。上图中的线程1和线程2所形成的就是循环等待。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、模拟并体验死锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在了解什么是死锁及其产生的条件后,我们根据上图中的死锁情景,通过一段代码来模拟体验死锁的发生。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根据上图所示,定义哪吒线程,在运行时将持有","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"A","attrs":{}},{"type":"text","text":"并请求","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"B","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static class NeZha implements Runnable {\n public void run() {\n synchronized(lockA) {\n System.out.println(\"哪吒: 持有A!\");\n\n try {\n Thread.sleep(10);\n } catch (InterruptedException ignored) {}\n System.out.println(\"哪吒: 等待B...\");\n\n synchronized(lockB) {\n System.out.println(\"哪吒: 已经同时持有A和B...\");\n }\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定义兰陵王线程,在运行时持有","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"B","attrs":{}},{"type":"text","text":"并请求","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"A","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static class LanLingWang implements Runnable {\n public void run() {\n synchronized(lockB) {\n System.out.println(\"兰陵王: 持有B!\");\n\n try {\n Thread.sleep(10);\n } catch (InterruptedException ignored) {}\n System.out.println(\"兰陵王: 等待A...\");\n\n synchronized(lockA) {\n System.out.println(\"兰陵王: 已经同时持有A和B...\");\n }\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"启动两个线程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class DeadLockDemo {\n public static final Object lockA = new Object();\n public static final Object lockB = new Object();\n\n public static void main(String args[]) {\n Thread thread1 = new Thread(new NeZha());\n Thread thread2 = new Thread(new LanLingWang());\n thread1.start();\n thread2.start();\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"两个线程的输出结果如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"哪吒: 持有A!\n兰陵王: 持有B!\n哪吒: 等待B...\n兰陵王: 等待A...\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从结果中可以看到,哪吒和兰陵王分别持有了A和B,但他们又相互请求对方持有的资源,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最终导致死锁,两个线程进入了无限地等待","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、死锁的处理","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 忽略死锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"忽略死锁是一种鸵鸟政策,它假设永远不会发生死锁。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"这种策略适用于死锁发生概率较低且影响可容忍的场景,如果死锁被证明永远不会发生也可以采用这种策略","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 检测","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这种策略下,死锁是允许发生的。如果系统检测到死锁,也会对其进行纠正,比如跟踪线程状态和资源分配。在死锁时,可以通过一些方法进行纠正:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"线程终止","attrs":{}},{"type":"text","text":":选择其中一个或多个线程进行终止,释放资源,打破死锁状态;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"资源抢占","attrs":{}},{"type":"text","text":":重新分配各线程已经抢占的资源,直到打破死锁。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 预防","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"对待死锁问题,预防是关键","attrs":{}},{"type":"text","text":"。本文第二小节已经列举死锁产生的一些必要条件,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"所以如果要预防死锁,只要打破其中任一条件即可","attrs":{}},{"type":"text","text":",Java中具体的死锁预发方式我们会在后面的文章中介绍。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上就是关于死锁的全部内容。在本文中,我们介绍了什么是死锁,以及死锁产生的必要条件和应对策略。对待开发中的死锁问题,既要保持敬畏之心,也不必闻之色变,审慎分析死锁的可能并设计合理策略可以有效预防死锁。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正文到此结束,恭喜你又上了一颗星✨","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"夫子的试炼","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行本文的示例代码,尝试找到破解其死锁的方法。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"延伸阅读与参考资料","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://en.wikipedia.org/wiki/Deadlock","title":"","type":null},"content":[{"type":"text","text":"死锁","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"《Java Concurrency in Practice》","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://en.wikipedia.org/wiki/Deadlock_prevention_algorithms","title":"","type":null},"content":[{"type":"text","text":"死锁预防算法","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.cn/post/6967277362455150628","title":"","type":null},"content":[{"type":"text","text":"《并发王者课》大纲与更新进度总览","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"关于作者","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关注公众号【","attrs":{}},{"type":"link","attrs":{"href":"https://writting.oss-cn-beijing.aliyuncs.com/2021/05/18/qrcodeforgh61bfe45f82b7200.jpg","title":"","type":null},"content":[{"type":"text","text":"庸人技术笑谈","attrs":{}}]},{"type":"text","text":"】,获取及时文章更新。记录平凡人的技术故事,分享有品质(尽量)的技术文章,偶尔也聊聊生活和理想。不贩卖焦虑,不做标题党。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果本文对你有帮助,欢迎","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"点赞","attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"关注","attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"监督","attrs":{}},{"type":"text","text":",我们一起","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"从青铜到王者","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章