死锁 基础

有一天张三来面试,
面试官:请你谈谈对死锁的理解
张三:你先给我offer我再说
面试官:你得把我的问题打上来我才能有机会给你offer啊

其实这就涉及到死锁了,张三要offer,面试官要死锁的解释,他俩谁都不松口的话,那么大概率就是面试官拿不到死锁的解释,然后挂掉张三,接着面下一个人,但是如果张三松口,提前说出了死锁的定义,那么他还是有机会看到offer的


死锁:多个进行相互等待对方资源,在得到所有资源继续运行之前,都不会释放自己已有的资源,这样造成了循环等待的现象,称为死锁。

一、 产生死锁的必要条件

  1. 互斥:某个资源一次只能被一个进程使用
  2. 请求和保持:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源
  3. 不可剥夺:进程在所获得的资源未使用完毕之前,不能被其他进程强行剥夺,只能等获得该资源的进程主动释放
  4. 环路等待:多个进程之间形成头尾衔接循环等待资源的关系

二、死锁的解决和预防

  • 死锁检测解决
         检测:首先为每个进程和每个资源指定一个唯一的号码,然后建立资源分配表和进程等待表。
         当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:
    • 剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
    • 撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
  • 死锁的预防
    • 破坏请求条件:资源一次性分配:一次性分配所有资源,这样就不会再有请求了(银行家算法);
    • 破坏请保持条件:只要有一个资源得不到分配,也不给这个进程分配其他的资源;
    • 破坏不可剥夺条件:可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源;
    • 破坏环路等待条件:资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反

实现预防死锁的各种方式:
1. 银行家算法(一次性给足够的资源)
2. 避免一个线程同时获取多个锁。
3. 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
4. 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
5. 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
6. 以确定的顺序获得锁
        如果必须获取多个锁,那么在设计的时候需要充分考虑不同线程之前获得锁的顺序。按照上面的例子,两个线程获得锁的时序图如下:

如果此时把获得锁的时序改成:

        那么死锁就永远不会发生。 针对两个特定的锁,开发者可以尝试按照锁对象的hashCode值大小的顺序,分别获得两个锁,这样锁总是会以特定的顺序获得锁,那么死锁也不会发生。问题变得更加复杂一些,如果此时有多个线程,都在竞争不同的锁,简单按照锁对象的hashCode进行排序(单纯按照hashCode顺序排序会出现“环路等待”),可能就无法满足要求了,这个时候开发者可以使用银行家算法,所有的锁都按照特定的顺序获取,同样可以防止死锁的发生。
7. 超时放弃
        当使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。 还是按照之前的例子,时序图如下:

三、死锁避免实际情况

  • 预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全的状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。
  • 银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此,状态包含两个向量Resource(系统中每种资源的总量)和Available(未分配给进程的每种资源的总量)及两个矩阵Claim(表示进程对资源的需求)和Allocation(表示当前分配给进程的资源)。安全状态是指至少有一个资源分配序列不会导致死锁。当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程知道同意该请求后系统状态仍然是安全的。

四、死锁检测

1、Jstack命令

jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。 Jstack工具可以用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

2、JConsole工具

Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值