文档章节

【14】死锁

秋雨霏霏
 秋雨霏霏
发布于 2017/09/09 17:12
字数 1356
阅读 28
收藏 1
点赞 0
评论 0

线程死锁

当两个或者多个线程互相阻塞等待对方持有的锁的时候,就会发生死锁。 也就是说,线程在申请某个锁的同时,持有者其他线程需要的锁,这通常是由于加锁的顺序不一致而导致的。

举例来说,如果线程1已经持有着锁A,然后再申请锁B;同时,线程2已经持有了锁B,然后申请锁A,这样就出现了死锁。 线程1永远得不到锁B,而线程2也永远得不到锁A。 关键是,两个线程对此还一无所知。 两个线程还是会继续持有着已获得的锁并一直等着另一个锁。这就是死锁。

两个线程的逻辑,如下所示:

Thread 1  locks A, waits for B
Thread 2  locks B, waits for A

下面这个例子,展示了一个TreeNode类,这个类上的方法都是synchronized的。

public class TreeNode {
 
  TreeNode parent   = null;  
  List     children = new ArrayList();

  public synchronized void addChild(TreeNode child){
    if(!this.children.contains(child)) {
      this.children.add(child);
      child.setParentOnly(this);
    }
  }
  
  public synchronized void addChildOnly(TreeNode child){
    if(!this.children.contains(child){
      this.children.add(child);
    }
  }
  
  public synchronized void setParent(TreeNode parent){
    this.parent = parent;
    parent.addChildOnly(this);
  }

  public synchronized void setParentOnly(TreeNode parent){
    this.parent = parent;
  }
}

由于内部还定义了一个parent属性,在synchronized方法中还对parent的方法进行了调用。由于都是TreeNode类型,synchronized实际是对实例的监控器锁的操作。这就会导致当前对象的synchronized方法,调用另一个对象的synchronized方法。 这实际上就成了,在持有当前实例的监控器锁的同时,去申请另一个实例的监控器锁。而这种情况,就可能会导致死锁的发生。

想象一下,如果线程1在parent实例上调用了parent.addChild(child)方法,同时,线程2在parent的子节点child上调用了child.setParent(parent)方法,那就产生了一个死锁。 来分析一下:

Thread 1: parent.addChild(child); //locks parent
          --> child.setParentOnly(parent);

Thread 2: child.setParent(parent); //locks child
          --> parent.addChildOnly()

首先,线程1调用parent.addChild(child)方法,因为方法是synchronized,所以,线程1也就持有了parent上的锁。

然后,线程2调用child.setParent(parent)方法,同样是一个synchronized方法,所以,线程2也就获得了child对象上的锁。

这样,parent和child上的锁,就分别被线程1和线程2获得。但是接下来,线程1就会去调用child.setParentOnly(),然而child上的锁已经被线程2获得了,所以线程1只好阻塞等待。 同样的,线程2接下来会去调用parent.addChildOnly(),而parent上锁正被线程1所持有,所以线程2也只好阻塞等待。 这样就导致了,线程1在等待线程2释放child上的锁;线程2在等待线程1释放parent上的锁。

注意:只有在线程1和线程2同时调用,才会死锁。例如,如果线程1稍稍领先于线程2,那就会使得线程1会顺利的获得锁A和B,而线程2会阻塞等待B。这样就不会死锁了。 而由于线程的调度是不可预知的,所以无法准确的说死锁会什么时候发生。我们只能说,这样的逻辑会导致死锁。

更复杂的死锁

死锁不仅仅是只有两个线程才会发生。实际中,情况会更为复杂,这也是为啥说死锁比较难排查的原因。 比如来看看下面这个情况:

Thread 1  locks A, waits for B
Thread 2  locks B, waits for C
Thread 3  locks C, waits for D
Thread 4  locks D, waits for A

这就像一个有向图中的圈。如果把线程比作图中顶点,等待另一个线程的锁比作一条边,如果这个有向图中形成了圈,那就意味着死锁。

数据库死锁

数据库中的事务,死锁会变得更为复杂。 数据库事务可能会包含多条SQL update 请求。 当事务中的某条记录被更新时,这条记录可能正被其他事务加锁。这样当前事务就需要等待加锁事务的完成。

而一个事务中每一个更新请求都可能需要对某些记录进行加锁处理。 这样同时执行的多个事务就可能需要对相同的记录进行更新,这样就会产生死锁风险。 例如:

Transaction 1, request 1, locks record 1 for update
Transaction 2, request 1, locks record 2 for update
Transaction 1, request 2, tries to lock record 2 for update.
Transaction 2, request 2, tries to lock record 1 for update.

由于加锁动作发生于不同的请求,而且当前事务无法预知全部的锁信息,所以,数据库事务发生死锁变得更难发现。


补充几点

可以对死锁的情况,做出一些整理:

  • 锁顺序死锁(lock-ordering deadlock)
    • 两个线程试图通过不同的顺序获得多个相同的锁
    • 注意外部参数引起的顺序变化
    • 致命的拥抱(deadly embrace)
      • 加锁顺序导致持有着对方的锁,并等待对方释放自己需要的锁
    • 环路的依赖关系
    • 持有锁,然后调用外部方法
      • 外部方法不知道会不会在其他对象上加锁
  • 资源死锁(resource deadlock)
    • 资源阻塞
    • 线程饥饿死锁(thread-starvation deadlock)

© 著作权归作者所有

共有 人打赏支持
秋雨霏霏
粉丝 143
博文 90
码字总数 160569
作品 0
杭州
CTO(技术副总裁)
程序员的自我修养——操作系统篇

目录: 1. 进程的有哪几种状态,状态转换图,及导致转换的事件。 2. 进程与线程的区别。 3. 进程通信的几种方式。 4. 线程同步几种方式。 5. 线程的实现方式. (用户线程与内核线程的区别) 6...

马浩 ⋅ 2014/06/30 ⋅ 0

嵌套管程锁死

嵌套管程锁死类似于死锁, 下面是一个嵌套管程锁死的场景: 你可以能会说,这是个空想的场景,好吧,让我们来看看下面这个比较挫的Lock实现: 可以看到,lock()方法首先在”this”上同步,然...

LCZ777 ⋅ 2014/05/22 ⋅ 0

xunsearch-1.4.6 中文全文检索,消除内存BUG

经过我们和用户的齐心努力,消除了迄今发现的所有异常退出、死锁、内存泄露等BUG,迫不及待地在今天发布 xunsearch-1.4.6 正式稳定版,以便让大家立即享受最畅快稳定的 xunsearch 搜索。 这是...

hightman ⋅ 2013/03/27 ⋅ 9

MySQL数据库InnoDB存储引擎源代码调试跟踪分析

导读目录: 1 早期结论... 3 2 测试一:死锁检测... 4 3 测试二:cursor测试... 6 4 测试三:external_lock测试... 6 5 测试四:杂项测试... 6 6 测试五:autocommit测试... 7 7 测试六:unl...

吴问志 ⋅ 2011/12/06 ⋅ 0

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是c...

刘桂林 ⋅ 2016/06/06 ⋅ 0

并发编程死锁的产生与范例分析

1.关于死锁 当两个以上的运算单元,双方都在等待对方停止运行,以获取系统资源,但是没有一方提前退出时,这种状况,就称为死锁。在多任务操作系统中,操作系统为了协调不同进程,能否获取系...

范大脚脚 ⋅ 2017/11/22 ⋅ 0

WSFC资源死锁案例

之前在WSFC日志分析进阶篇中曾经提到过一些关于WSFC底层原理,例如Resource.dll,RHS,RCM,了解这些组件对于我们后期做群集排错有莫大的帮助,本文我们就通过一个实际的资源死锁的案例,来帮...

老收藏家 ⋅ 2017/10/20 ⋅ 0

Java多线程顺序有序执行

package coderr.kerwin.codetest.test; public class Test { public static void main(String[] args) throws InterruptedException {TempObject obj1 = new TempObject(1,5);TempObject obj......

ileler ⋅ 2015/07/07 ⋅ 6

操作系统:精髓与设计原理(原书第6版).PDF

操作系统:精髓与设计原理(原书第6版) 书名原文:Operating Systems: Internals and Design Principles, Sixth Edition 作者:(美)斯托林斯(Stallings,W.) 译者:陈向群 出版社:机械工...

刘静 ⋅ 2010/09/16 ⋅ 2

Chromium引擎控件DotNetBrowser V1.14发布 | 附下载

DotNetBrowser 1.14 更新 改进 在这个版本中,我们改进了JavaScript-.NET桥接和DOM事件处理的性能。 修复 编译引用DotNetBrowser的C ++/CLI应用程序导致的内部编译器错误,这个问题是由混淆器...

可乐瓶的小辣椒 ⋅ 01/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从 Confluence 5.3 及其早期版本中恢复空间

如果你需要从 Confluence 5.3 及其早期版本中的导出文件恢复到晚于 Confluence 5.3 的 Confluence 中的话。你可以使用临时的 Confluence 空间安装,然后将这个 Confluence 安装实例升级到你现...

honeymose ⋅ 今天 ⋅ 0

用ZBLOG2.3博客写读书笔记网站能创造今日头条的辉煌吗?

最近两年,著名的自媒体网站今日头条可以说是火得一塌糊涂,虽然从目前来看也遇到了一点瓶颈,毕竟发展到了一定的规模,继续增长就更加难了,但如今的今日头条规模和流量已经非常大了。 我们...

原创小博客 ⋅ 今天 ⋅ 0

MyBatis四大核心概念

本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper)。 MyBatis 作为互联网数据库映射工具界的“上古神器”,训有四大“神兽”,谓之:Sql...

waylau ⋅ 今天 ⋅ 0

以太坊java开发包web3j简介

web3j(org.web3j)是Java版本的以太坊JSON RPC接口协议封装实现,如果需要将你的Java应用或安卓应用接入以太坊,或者希望用java开发一个钱包应用,那么用web3j就对了。 web3j的功能相当完整...

汇智网教程 ⋅ 今天 ⋅ 0

2个线程交替打印100以内的数字

重点提示: 线程的本质上只是一个壳子,真正的逻辑其实在“竞态条件”中。 举个例子,比如本题中的打印,那么在竞态条件中,我只需要一个方法即可; 假如我的需求是2个线程,一个+1,一个-1,...

Germmy ⋅ 今天 ⋅ 0

Springboot2 之 Spring Data Redis 实现消息队列——发布/订阅模式

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式,这里利用redis消息“发布/订阅”来简单实现订阅者模式。 实现之前先过过 redis 发布订阅的一些基础概念和操...

Simonton ⋅ 今天 ⋅ 0

error:Could not find gradle

一.更新Android Studio后打开Project,报如下错误: Error: Could not find com.android.tools.build:gradle:2.2.1. Searched in the following locations: file:/D:/software/android/andro......

Yao--靠自己 ⋅ 昨天 ⋅ 0

Spring boot 项目打包及引入本地jar包

Spring Boot 项目打包以及引入本地Jar包 [TOC] 上篇文章提到 Maven 项目添加本地jar包的三种方式 ,本篇文章记录下在实际项目中的应用。 spring boot 打包方式 我们知道,传统应用可以将程序...

Os_yxguang ⋅ 昨天 ⋅ 0

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 昨天 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部