ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java并发编程-死锁(下):如何解决死锁

2021-04-24 06:32:11  阅读:197  来源: 互联网

标签:转账 占有 Java 账户 编程 死锁 线程 资源


转:

Java并发编程-死锁(下):如何解决死锁

我在上篇文章曾经提到,锁的本质是串行化,如果覆盖的范围太大,会导致程序的性能低下。

为了提升性能,我们用了细粒度锁,但这又带来了死锁问题。

如何解决死锁问题,就是程序员价值所在。

如何规避死锁

说实话,大部分情况下,你不需要考虑死锁问题。因为只有在并发量很大的时候,死锁才会出现。

那该怎么解决呢?很简单,重启应用就行。

然而,问题来了。既然是在高并发场景下,才会出现死锁。那这不就意味着,一旦出现死锁,无论重启多少次,程序也没法运行?

因为只要并发量一大,你就得重启应用,但原来的业务没处理完,现在得接着处理呀。结果就是,等重启完了,并发量再次飙升,死锁再次出现,你又得重启应用。如此循环往复,根本看不到头。

因此,想要解决死锁,唯一可行的办法是:规避死锁,不让死锁出现。

如何规避死锁?这个问题,早在 1971 年,就有位叫 Coffman 的大神提出了解决思路。

死锁的发生要同时满足四个条件。换句话说,我们只要破坏其中一个,就可以避免死锁。这四个条件分别是:

  1. 互斥:在同一时刻,一个资源只能由一个线程操作;
  2. 占有且等待:线程占有了一个资源,在申请另一个资源的时候,不释放自己占有的资源;
  3. 不可抢占:资源被某个线程占有后,其它线程不能强行抢占;
  4. 循环等待:线程一占有了一个资源,线程二占有了另一个资源,它们都在等对方的资源。

这四个条件必须同时满足,才会发生死锁。我们只要想办法破坏其中一个,就能避免死锁的出现。

既然如此,该破坏哪个条件呢?

首先,破坏条件一互斥,这是没法做到的,毕竟用锁就是为了互斥。

然后,破坏条件三不可抢占,现在也做不到。因为这需要线程主动释放占有的资源,但synchronized 可做不到这点,得用到 Java 的并发包。所以,我们以后再聊。

那么,方案只剩两个了。

破坏占有且等待条件

所谓占有且等待就是,运行程序需要两个以上的资源,那么线程要把这些资源都拿到手,才有资格运行程序。

然而,线程又不止一个,资源就放在那儿,谁抢到就是谁,就可以一直占着,不释放出去。

这时候,如果线程只抢到了一个资源,没资格运行程序,但又不释放自己的资源,就在那儿干等着。那其它线程怎么办?只能陪你一起等,谁都没法动呗。

这种僵持的局面怎么破?

一次性锁定所有资源,这样就能破坏占有且等待条件。

拿前面的转账来说,它需要锁定两个资源,一个是转出账户,一个是转入账户。如果一次只锁定一个账户,下一个账户又锁定不了,死锁不就出现了。

那这样行不行,我一次性锁定所有账户。

在转账之前,我同时申请两个账户,而且必须同时加锁成功时,才能继续转账操作。

在设计思路上,我们可以把代码分成两块:第一块,是业务员模块,负责具体的转账业务;第二块,是管理员模块,负责资源的审批。你看下这副图:

Java并发编程-死锁(下):如何解决死锁

简单来说,业务员提交申请,账户管理员锁定资源。

比如说,张三想要转账,要锁定账户A账户B,但账户管理员发现文件架上只有账户A,那张三只能等着,直到账户A账户B都回来时,才能进行加锁。

思路有了,接下来,我们就转换成代码吧。

class Account {
    private int balance;

    // 转账
    void transfer(Account target, int amt) {
        // 一次性申请资源:转出账户、转入账户,不断循环
        while (!Allocator.getInstance().apply(this, target));

        // 执行转账
        if (this.balance >= amt) {
            this.balance -= amt;
            target.balance += amt;
        }

        // 归还资源:转出账户、转入账户
        Allocator.getInstance().free(this, target);
    }
}

class Allocator {

    // 单例对象,我就不写了,相信你能搞定
    static Allocator getInstance() {
        return null;
    }

    // 资源账本:记录哪些资源不能用
    private Set

标签:转账,占有,Java,账户,编程,死锁,线程,资源
来源: https://www.cnblogs.com/wangtcc/p/14696147.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有