精华内容
下载资源
问答
  • 死锁银行家算法 ; 1.死锁概念 ;思考; 产生死锁的原因 ; 1. 竞争系统资源 ;由于进程的调度是独立的请求和释放操作可按如下序列进行 Ar1, Ar2, Ar3, Ar4, Br1, Br2, Br3, Br4 Br1, Br2, Br3, Br4, Ar1, Ar2, Ar3, Ar4 ...
  • 主要介绍了Mysql使用kill命令解决死锁问题(杀死某条正在执行的sql语句)的相关资料,需要的朋友可以参考下
  • 死锁也是一样的,如果线上发生死锁问题,为了尽快减小损失,最好的办法是保存 JVM 信息、日志等“案发现场”的数据,然后立刻重启服务,来尝试修复死锁。为什么说重启服务能解决这个问题呢?因为发生死锁往往要有很...

    线上发生死锁应该怎么办

    如果线上环境发生了死锁,那么其实不良后果就已经造成了,修复死锁的最好时机在于“防患于未然”,而不是事后补救。就好比发生火灾时,一旦着了大火,想要不造成损失去扑灭几乎已经不可能了。死锁也是一样的,如果线上发生死锁问题,为了尽快减小损失,最好的办法是保存 JVM 信息、日志等“案发现场”的数据,然后立刻重启服务,来尝试修复死锁。为什么说重启服务能解决这个问题呢?因为发生死锁往往要有很多前提条件的,并且当并发度足够高的时候才有可能会发生死锁,所以重启后再次立刻发生死锁的几率并不是很大,当我们重启服务器之后,就可以暂时保证线上服务的可用,然后利用刚才保存过的案发现场的信息,排查死锁、修改代码,最终重新发布

    常见修复策略

    • 避免策略
    • 检测与恢复策略
    • 鸵鸟策略
    • 避免策略

    避免策略最主要的思路就是,优化代码逻辑,从根本上消除发生死锁的可能性。通常而言,发生死锁的一个主要原因是顺序相反的去获取不同的锁。因此我们就演示如何通过调整锁的获取顺序来避免死锁

    转账时避免死锁

    我们先来看一下转账时发生死锁的情况。这个例子是一个示意性的,是为了学习死锁所而写的例子,所以和真实的银行系统的设计有很大不同,因为我们主要看的是如何避免死锁,而不是转账的业务逻辑

    (1)发生了死锁

    我们的转账系统为了保证线程安全,在转账前需要首先获取到两把锁(两个锁对象),分别是被转出的账户和被转入的账户。如果不做这一层限制,那么在某一个线程修改余额的期间,可能会有其他线程同时修改该变量,可能导致线程安全问题。所以在没有获取到这两把锁之前,是不能对余额进行操作的;只有获取到这两把锁之后,才能进行接下来真正的转账操作。当然,如果要转出的余额大于账户的余额,也不能转账,因为不允许余额变成负数

    而这期间就隐藏着发生死锁的可能,我们来看下代码

    public class TransferMoney implements Runnable {
    
        int flag;
        static Account a = new Account(500);
        static Account b = new Account(500);
    
        static class Account {
    
            public Account(int balance) {
                this.balance = balance;
            }
    
            int balance;
        }
    
        @Override
        public void run() {
            if (flag == 1) {
                transferMoney(a, b, 200);
            }
            if (flag == 0) {
                transferMoney(b, a, 200);
            }
        }
    
        public static void transferMoney(Account from, Account to, int amount) {
            //先获取两把锁,然后开始转账
            synchronized (to) {
                synchronized (from) {
                    if (from.balance - amount < 0) {
                        System.out.println("余额不足,转账失败。");
                        return;
                    }
                    from.balance -= amount;
                    to.balance += amount;
                    System.out.println("成功转账" + amount + "元");
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            TransferMoney r1 = new TransferMoney();
            TransferMoney r2 = new TransferMoney();
            r1.flag = 1;
            r2.flag = 0;
            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r2);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("a的余额" + a.balance);
            System.out.println("b的余额" + b.balance);
            }
        }
    

    首先定义了 int 类型的 flag,它是一个标记位,用于控制不同线程执行不同逻辑。然后建了两个 Account 对象 a 和 b,代表账户,它们最初都有 500 元的余额

    我们接下来看 run 方法,该方法里面会根据 flag 值,来决定传入 transferMoney 方法的参数的顺序,如果 flag 为 1,那么就代表从 a 账户转给 b 账户 200元;相反,如果 flag 为 0,那么它就从 b 账户转给 a 账户 200 元

    我们再来看一下 transferMoney 转账方法,这个方法会先尝试获取两把锁,也就是 synchronized (to) 和 synchronized (from)。当都获取成功之后,它首先会判断余额是不是足以转出本次的转账金额,如果不足的话,则直接用 return 来退出;如果余额足够,就对转出账户进行减余额,对被转入的账户加余额,最后打印出“成功转账 XX 元”的消息

    在主函数中我们新建了两个 TransferMoney 对象,并且把它们的 flag 分别设置为 1 和 0,然后分别传入两个线程中,并把它们都启动起来,最后,打印出各自的余额

    执行结果如下

    成功转账200元
    成功转账200元
    a的余额500
    b的余额500
    

    代码是可以正常执行的,打印结果也是符合逻辑的。此时并没有发生死锁,因为每个锁的持有时间很短,同时释放也很快,所以在低并发的情况下,不容易发生死锁的现象。那我们对代码做一些小调整,让它发生死锁

    如果我们在两个 synchronized 之间加上一个 Thread.sleep(500),来模拟银行网络迟延等情况,那么 transferMoney 方法就变为

    public static void transferMoney(Account from, Account to, int amount) {
        //先获取两把锁,然后开始转账
        synchronized (to) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (from) {
                if (from.balance - amount < 0) {
                    System.out.println("余额不足,转账失败。");
                    return;
                }
                from.balance -= amount;
                to.balance += amount;
                System.out.println("成功转账" + amount + "元");
            }
        }
    }
    

    可以看到 transferMoney 的变化就在于,在两个 synchronized 之间,也就是获取到第一把锁后、获取到第二把锁前,我们加了睡眠 500 毫秒的语句。此时再运行程序,会有很大的概率发生死锁,从而导致控制台中不打印任何语句,而且程序也不会停止

    我们分析一下它为什么会发生死锁,最主要原因就是,两个不同的线程获取两个锁的顺序是相反的(第一个线程获取的这两个账户和第二个线程获取的这两个账户顺序恰好相反,第一个线程的“转出账户”正是第二个线程的“转入账户”),所以我们就可以从这个“相反顺序”的角度出发,来解决死锁问题

    (2)实际上不在乎获取锁的顺序

    经过思考,我们可以发现,其实转账时,并不在乎两把锁的相对获取顺序。转账的时候,我们无论先获取到转出账户锁对象,还是先获取到转入账户锁对象,只要最终能拿到两把锁,就能进行安全的操作。所以我们来调整一下获取锁的顺序,使得先获取的账户和该账户是“转入”或“转出”无关,而是使用 HashCode 的值来决定顺序,从而保证线程安全

    public static void transferMoney(Account from, Account to, int amount) {
    
        int fromHash = System.identityHashCode(from);
        int toHash = System.identityHashCode(to);
        if (fromHash < toHash) {
            synchronized (from) {
                synchronized (to) {
                    if (from.balance - amount < 0) {
                        System.out.println("余额不足,转账失败。");
                        return;
                    }
                    from.balance -= amount;
                    to.balance += amount;
                    System.out.println("成功转账" + amount + "元");
                }
            }
        } else if (fromHash > toHash) {
            synchronized (to) {
                synchronized (from) {
                    if (from.balance - amount < 0) {
                        System.out.println("余额不足,转账失败。");
                        return;
                    }
                    from.balance -= amount;
                    to.balance += amount;
                    System.out.println("成功转账" + amount + "元");
                }
            }
        }
    }
    

    可以看到,我们会分别计算出这两个 Account 的 HashCode,然后根据 HashCode 的大小来决定获取锁的顺序。这样一来,不论是哪个线程先执行,不论是转出还是被转入,它获取锁的顺序都会严格根据 HashCode 的值来决定,那么大家获取锁的顺序就一样了,就不会出现获取锁顺序相反的情况,也就避免了死锁

    (3)有主键就更安全、方便

    用主键决定锁获取顺序的方式,它会更加的安全方便。刚才我们使用了 HashCode 作为排序的标准,因为 HashCode 比较通用,每个对象都有,不过这依然有极小的概率会发生 HashCode 相同的情况。在实际生产中,需要排序的往往是一个实体类,而一个实体类一般都会有一个主键 ID,主键 ID 具有唯一、不重复的特点,所以如果我们这个类包含主键属性的话就方便多了,我们也没必要去计算 HashCode,直接使用它的主键 ID 来进行排序,由主键 ID 大小来决定获取锁的顺序,就可以确保避免死锁

     

    • 检测与恢复策略

    什么是死锁检测算法

    它和之前避免死锁的策略不一样,避免死锁是通过逻辑让死锁不发生,而这里的检测与恢复策略,是先允许系统发生死锁,然后再解除。例如系统可以在每次调用锁的时候,都记录下来调用信息,形成一个“锁的调用链路图”,然后隔一段时间就用死锁检测算法来检测一下,搜索这个图中是否存在环路,一旦发生死锁,就可以用死锁恢复机制,比如剥夺某一个资源,来解开死锁,进行恢复。所以它的思路和之前的死锁避免策略是有很大不同的

    在检测到死锁发生后,如何解开死锁呢?

    方法1——线程终止

    第一种解开死锁的方法是线程(或进程,下同)终止,在这里,系统会逐个去终止已经陷入死锁的线程,线程被终止,同时释放资源,这样死锁就会被解开

    当然这个终止是需要讲究顺序的,一般有以下几个考量指标

    (1)优先级

    一般来说,终止时会考虑到线程或者进程的优先级,先终止优先级低的线程。例如,前台线程会涉及界面显示,这对用户而言是很重要的,所以前台线程的优先级往往高于后台线程

    (2)已占用资源、还需要的资源

    同时也会考虑到某个线程占有的资源有多少,还需要的资源有多少?如果某线程已经占有了一大堆资源,只需要最后一点点资源就可以顺利完成任务,那么系统可能就不会优先选择终止这样的线程,会选择终止别的线程来优先促成该线程的完成

    (3)已经运行时间

    另外还可以考虑的一个因素就是已经运行的时间,比如当前这个线程已经运行了很多个小时,甚至很多天了,很快就能完成任务了,那么终止这个线程可能不是一个明智的选择,我们可以让那些刚刚开始运行的线程终止,并在之后把它们重新启动起来,这样成本更低

    方法2——资源抢占

    其实,我们不需要把整个的线程终止,而是只需要把它已经获得的资源进行剥夺,比如让线程回退几步、 释放资源,这样一来就不用终止掉整个线程了,这样造成的后果会比刚才终止整个线程的后果更小一些,成本更低

    当然这种方式也有一个缺点,那就是如果算法不好的话,我们抢占的那个线程可能一直是同一个线程,就会造成线程饥饿。也就是说,这个线程一直被剥夺它已经得到的资源,那么它就长期得不到运行

     

    • 鸵鸟策略

    鸵鸟策略,鸵鸟策略以鸵鸟命名,因为鸵鸟有一个特点,就是遇到危险的时候,它会把头埋到沙子里,这样一来它就看不到危险了

    鸵鸟策略的意思就是,如果我们的系统发生死锁的概率不高,并且一旦发生其后果不是特别严重的话,我们就可以选择先忽略它。直到死锁发生的时候,我们再人工修复,比如重启服务,这并不是不可以的。如果我们的系统用的人比较少,比如是内部的系统,那么在并发量极低的情况下,它可能几年都不会发生死锁。对此我们考虑到投入产出比,自然也没有必要去对死锁问题进行特殊的处理,这是需要根据我们的业务场景进行合理选择的

    展开全文
  • 什么是死锁? 锁,顾名思义,含义真的就是我们平常每天看到的那个锁,锁门的锁,如果门锁着,那就进不去了,那就只能在门外等着。 软件中的锁,意义和这个类似,也是为了阻止非授权用户能够进入某些代码的执行,...

    什么是死锁?

    锁,顾名思义,含义真的就是我们平常每天看到的那个锁,锁门的锁,如果门锁着,那就进不去了,那就只能在门外等着。

    软件中的锁,意义和这个类似,也是为了阻止非授权用户能够进入某些代码的执行,如果要想执行被锁保护(同步)的代码,那么必须要先获得锁,如果你想进去的时候,别人正在拥有这个锁,你也只好等待了,必须等到被人使用完了以后才能进入被保护或者被同步的代码执行。

    什么是死锁呢?

    死锁是指两个或两个以上的线程或者进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,而且会一直阻塞下去,此时称软件或者系统处于死锁状态。

    为什么会死锁?

    我们来看一张图

     

    这里有两个线程,线程A和线程B,这里也有两把锁,锁1和锁2,线程A和线程B分别按照自己的方式来使用这两把锁,线程1先获取锁1,做一些事情,然后获取锁2,再做一些事情,最后释放锁2,释放锁1,线程B先获取锁2,做一些事情,然后获取锁1,再做一些事情,最后释放锁1,释放锁2.

    看起来没有问题,而且往往执行起来也没有什么问题,但是,恰恰就是这样的使用锁,就是导致死锁的原因之一。

    我们假设线程A获得锁1的同时线程B也获得了锁2,一旦这种情况发生,就发生了死锁,为什么呢?

    因为线程A接下来要去获取锁2,因为线程B还没有释放锁2,所以线程A只好等待,同样线程B做了一些事情之后,需要再去获取锁1,同样的原因,线程A还没有释放锁!,因为还在等待锁2呢,所以,线程A和线程B就进入了死锁状态。会一直等待下去,谁也得不到自己想要的锁。

    如何解决死锁呢?

    要解决死锁问题,必须要正确使用锁,可以从两个方面来解决死锁问题:

    1.一个线程使用多个锁的时候,使用顺序保持一致

    2.一个线程使用多个锁的时候,锁尽量不要交叉,一个锁用完,立即释放后再去使用另外一个锁。

     

    只要注意了这两点,基本上就不会死锁了,当然如果使用不当,比如循环用锁等,递归使用等,也是有可能发生死锁的。

    如何调试死锁?

    VC和Windbg都提供了很好的调试死锁的方法,这里有一个视频是关于VC如何调试死锁问题的,如果有兴趣,可以看一下。

    https://edu.csdn.net/course/detail/28915

     

     

    展开全文
  • 解决死锁问题的基本方法

    千次阅读 2020-11-18 12:54:52
    当多个进程同时竞争一种资源而都又缺少资源无法完成任务时,就会造成一种僵局,多个进程每个都抢占了一部分但还缺少一部分,这些进程无法向前推进,也无法后退,这就是死锁。 一、合理分配资源 首先,第一种方法...

    当多个进程同时竞争一种资源而都又缺少资源无法完成任务时,就会造成一种僵局,多个进程每个都抢占了一部分但还缺少一部分,这些进程无法向前推进,也无法后退,这就是死锁。

    一、合理分配资源

    首先,第一种方法就是预防死锁的产生,一次性合理地分配所有的资源,只要有一个资源得不到分配,也不给这个进程分配其他资源。这一方面主要就是预防死锁条件的产生。可以使用银行家算法,合理分配资源。

    二、允许抢占资源

    第二种就是发现系统中有进程死锁时,我们可以强制性地剥夺抢占某些进程的资源,然后分配给死锁进程,以解除死锁状态。用一部分死锁进程的资源来解决另外一部分进程的资源。

    三、撤销进程挂起

    在第二种中,我们是直接销毁某些死锁的进程来解除另外一部分进程的死锁。但是现在我们可以不用销毁这部分的死锁进程。我们可以将其挂起到CPU外,将资源空出来让给死锁进程,这样也可以解决死锁进程。

    展开全文
  • Linux -- 解决死锁问题

    2019-08-07 08:42:35
    使用管理员权限执行命令...根据指引找寻 .java文件中的对应行,并根据具体情况解决死锁 总结: jstack打印java进程所有线程的调用栈信息+相应的源代码的分析; 寻找 wait for… + 等待的锁资源的地址,wait...
    • 使用管理员权限执行命令(因为我的.java文件是由管理员创建的);在这里插入图片描述
    • 使用 jps 命令查询进程ID,jstack 命令查询进程的堆栈信息;
      在这里插入图片描述
    • 找到 waitingwait for字段;
      在这里插入图片描述在这里插入图片描述
      在这里插入图片描述
    • 根据指引找寻 .java文件中的对应行,并根据具体情况解决死锁
      在这里插入图片描述在这里插入图片描述
      总结
    • jstack打印java进程所有线程的调用栈信息+相应的源代码的分析;
    • 寻找 wait for… + 等待的锁资源的地址waiting to lock…+等待的锁资源的地址
    • 如果是死锁问题,会发现多个线程都进入的waiting状态,调用栈信息最上面都会显示如wait forwaiting to lock这样的关键字,表示 当前线程在等待一个无法获取的锁资源;
    • 此时结合调用栈信息和代码,具体分析这把锁还在那里获取过?为什么释放不了?
    展开全文
  • 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。在一个进程的集合中,每个进程都在等待只能由该集合中其他进程才能引发的事件,那么该进程集合就是死锁的。 **死锁产生原因:[1] ...
  • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为...
  • 解决死锁问题-设置加锁顺序

    千次阅读 2019-02-19 14:11:41
    大部分死锁都是由于不恰当的加锁顺序造成的,例如,线程A持有lock1等待lock2,而线程B持有lock2等待lock1,解决办法是每个线程都以相同的顺序获取多个锁。 先看一个由于错误的加锁顺序导致的死锁: public class ...
  • 使用redis做分布式锁时大家可能都知道使用setnx容易发生死锁情况,大多数都会推荐使用redission来实现,那么redission是如何解决死锁问题的呢? 有关Redisson作为实现分布式锁,总的分3大模块来讲。 1. `1、...
  • 解决死锁问题的三种方法:预防死锁,检测死锁及避免死锁。
  • 银行家算法解决死锁问题

    万次阅读 2017-04-10 11:23:53
    银行家算法解决死锁问题 一.概念引入 银行家算法( banker's algorithm )由 Dijkstra于1965提出,关键是将死锁的问题演示为一个银行家贷款的模型,由于能用于银行系统的现金贷款而出名。一个银行家向一群客户发放...
  • Java解决死锁问题eclipse代码版
  • MySQL死锁问题是很多程序员在项目开发中常遇到的问题,现就MySQL死锁及解决方法详解如下: 1、MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表级...
  • 如果出现了死锁的情况,我们需要怎么去解决呢?首先,我们来看下什么是死锁,下面这张图很形象的说明了什么是死锁,路口的四辆车都分别等待对面车道的车让行,相互等待,形成了死锁的状态。在线程中,死锁是这样定义...
  • 哲学家进餐问题三种解决死锁问题

    千次阅读 2019-09-15 16:47:50
    方案一 至多允许有四位哲学家同时去拿左边的筷子,然后在允许拿右边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能同时释放他用过的两只筷子,从而使更多的哲学家能够进餐 semaphore chopstick[5] = ...
  • Redisson(1)分布式锁——如何解决死锁问题

    万次阅读 热门讨论 2019-03-04 16:13:16
    Redisson是如何解决死锁问题的? 普通利用Redis实现分布式锁的时候,我们可能会为某个锁指定某个key,当线程获取锁并执行完业务逻辑代码的时候,将该锁对应的key删除掉来释放锁。 lock->set(key),成功->...
  • 在SQL Server 2005中解决死锁问题
  • 在使用mysql运行某些语句时,会因数据量太大而导致死锁,没有反映。这个时候,就需要kill掉某个正在消耗资源的query语句即可, KILL命令的语法格式如下:KILL [CONNECTION | QUERY] thread_id每个与mysqld的连接都...
  • 为了解决多线程死锁问题,不是不使用锁,而是用信号量去控制。 信号量可以控制资源能被多少线程访问,此处指定只能被一个线程访问,就实现了锁的功能;然而,信号量可以指定获取的超时时间,所以,可以根据这个超时...
  • 解决Oracle死锁问题

    2016-12-19 23:41:10
    Oracle解决死锁问题
  • 常听见的一名话是“死锁是程序问题,不能通过优化数据库来解决死锁,必须要改写程序,做好事务与访问顺序方面的控制”如果产生死锁的业务模块频繁访问大量堆表,通过数据库优化是在一定的概率下可以解决死锁问题的,...
  • Java 程序死锁问题原理及解决方案

    万次阅读 2016-11-08 21:04:00
    所以,对于死锁问题在理论上和技术上都必须予以高度重视。 银行家算法 一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产。...
  • 死锁是多线程编程或者说是并发编程中的一个经典问题,也是我们在实际工作中很可能会碰到的问题。相信大部分读者对“死锁”这个词都是略有耳闻的,但从我对后端开发岗位的面试情况来看很多同学往往对死锁都还没有系统...
  • 数据库死锁及解决死锁问题

    千次阅读 2015-11-14 10:04:30
    所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程.由于...
  • SQL SERVER-临时表解决死锁问题

    千次阅读 2019-01-31 22:22:31
    锁的级别在表级别。  X:排它锁 S:共享锁 ...操作表的顺序一致可以避免死锁 --多表连接 --假设:员工表:10000000000000 部门:10亿条 --使用临时表。 select * from Employee as E left join Po...
  • 在使用mysql运行某些语句时,会因数据量太大而导致死锁,没有反映。这个时候,就需要kill掉某个正在消耗资源的query语句即可, KILL命令的语法格式如下: KILL [CONNECTION | QUERY] thread_id 每个与mysqld的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 164,220
精华内容 65,688
关键字:

如何解决死锁问题