为了账号安全,请及时绑定邮箱和手机立即绑定

死锁的例子

死锁的例子

一只斗牛犬 2023-06-04 19:42:10
我检查了很多网站,唯一的例子deadlock就是这样。synchronized块中总有块synchronized。(方法withdraw被锁定a,方法deposit被锁定b。)class Account{    int balance;    Account(int amount)    {balance = amount;}    void withdraw(int amount)    {balance-=amount;}    void deposit(int amount)    {balance+=amount;}}class Threaddemo extends Thread{    Account a,b;    int amount;    Threaddemo(Account a,Account b,int amount)    {        this.a=a;this.b=b;this.amount=amount;        start();    }    public void run()    {        transfer(a,b,amount);    }    public void transfer(Account a,Account b,int amount)    {        synchronized(a)        {            a.withdraw(amount);            System.out.print(amount+" is withdrawn from account a\n");            try{Thread.sleep(500);}            catch(Exception e){System.out.println(e);}            synchronized(b)            {                b.deposit(amount);                System.out.print(amount+" is deposited into account b\n");            }        }    }}class U3{    public static void main(String[] args)     {        Account a = new Account(1000);        Account b = new Account(2000);        new Threaddemo(a,b,100);        new Threaddemo(b,a,200);    }}但是如果我们在一个同步块之后使用一个同步块,就不会有死锁。如果这是导致死锁的唯一方法,那我们为什么不使用两个单独的同步块呢?如果还有其他方法导致死锁,请举例说明。
查看完整描述

1 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

考虑一家拥有数千个类型的银行账户的银行Account。现在让我们看看为什么这段代码会导致死锁:


public void transfer(Account a,Account b,int amount)

    {

        synchronized(a)

        {

            a.withdraw(amount);

            System.out.print(amount+" is withdrawn from account a\n");


            try{Thread.sleep(500);}

            catch(Exception e){System.out.println(e);}


            synchronized(b)

            {

                b.deposit(amount);

                System.out.print(amount+" is deposited into account b\n");

            }

        }

    }

让有线程tA和线程tB。如果线程在线程同时运行时tA运行以下代码 ,则可能会出现死锁,因为如果我们查看以下顺序:transfer(accountA, AccountB)tBtransfer(accountB, accountA)

  1. 面向:synchronized(accountA)

  2. 待定:synchronized(accountB)

  3. tA:尝试锁定对象AccountB,但锁定由 tB 持有 =>死锁

我们看到两者之间存在循环依赖关系,不允许其中一个线程继续前进。

如果我们查看您更新的代码:

public void transfer(Account a,Account b,int amount)

    {

        synchronized(a)

        {

            a.withdraw(amount);

            System.out.print(amount+" is withdrawn from account a\n");


            try{Thread.sleep(500);}

            catch(Exception e){System.out.println(e);}

        }

        synchronized(b)

        {

            b.deposit(amount);

            System.out.print(amount+" is deposited into account b\n");

        }

    }

我们必须采取以下假设:

  • 账户 a 有无限的资金,因为它可能是 a.balance < amount,这意味着 a.balance < 0,这打破了我们总是有余额 >=0 的不变量。

    • 我们允许不一致,例如,如果我们想汇总所有当前现金,我们将汇总少于实际金额,因为您当前的代码允许我们这样做。

如果我们尝试修复代码,我们必须在更新余额之前确保 a.balance >= amount。现在让我们看看以下场景:

  1. 账户abalance < amount

  2. 我们必须等到a.balance >= amount从帐户中取款a

  3. 因为我们在这个线程中保持 Account 的锁a,所以没有其他线程可以更新a.balance=> 我们遭受饥饿

要解决这些问题,您要么必须使用监视器或条件来检查 a.balance>=amount 是否要进行,并将线程置于阻塞状态,以便线程可以进行或更新您的代码,以便锁定总是以相同的顺序获取。

解决方案#1:获取对象锁的唯一顺序

如果我们使用唯一的顺序获取对象的锁,我们可以确保不会发生死锁,因为我们以指定的顺序获取锁,不允许任何循环依赖,否则称为死锁。

public void transfer(Account a,Account b,int amount)

    {

       //define a specific order, in which locks are acquired

       //the id's of all accounts are unique!

       if(a.id<b.id){

          synchronized(a){

            synchronized(b){

               //do operations here

            }

          }

       }else{

          synchronized(b){

            synchronized(a){

               //do operations here

            }

          }

       }

    }

解决方案 #2:使用生产者-消费者模式来检查a.balance>=amount.


public void transfer(Account a,Account b,int amount)

{

    while(true){

      synchronized(a){

          if(a.balance>=amount){

              //do operations here

          }

      }


        try{Thread.sleep(500);} //Use this as a backoff, as otherwise you'd likely get high congestion

        catch(Exception e){System.out.println(e);}

    }

    synchronized(b)

    {

       //do operations here

    }

}


查看完整回答
反对 回复 2023-06-04
  • 1 回答
  • 0 关注
  • 107 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信