死锁的例子

Example of deadlock

我查了很多网站,只有deadlock的例子是这样的。 synchronized 块中总是有一个 synchronized 块。

(方法 withdrawa 锁定,方法 depositb 锁定。)

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);

    }
}

但是如果我们在同步块之后使用同步块,就不会出现死锁。

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);

    }
}

如果这是导致死锁的唯一方法,那我们为什么不使用两个单独的同步块呢? 如果还有其他的死锁方法请举例说明。

考虑一家拥有数千个 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) 当线程 tB 同时运行 transfer(accountB, accountA) 时,可能会出现死锁,因为如果我们查看以下顺序:

  1. tA: synchronized(accountA)

  2. tB: synchronized(accountB)

  3. tA: tries to lock object AccountB, but lock is held by tB => deadlock

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

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

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 >= 金额。现在让我们看看以下场景:

  1. Account a has balance < amount

  2. We must wait until a.balance >= amount to withdraw money from Account a

  3. As we are keeping the lock of Account a in this thread, no other thread can ever update a.balance => We suffer from starvation

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

解决方案 #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
    }
}