如何确保锁定顺序以避免死锁? [英] How to ensure locking-order to avoid deadlock?

查看:381
本文介绍了如何确保锁定顺序以避免死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假定下面的Account类有两个对象-account1和account2.并且有两个线程T1和T2.

Assume there are two objects of the following Account class - account1 and account2. And there are two threads T1 and T2.

T1如下将金额100从帐户1转移到帐户2:

T1 is transferring amount 100 from account1 to account2 as follows:

account1.transfer(account2, 100);

类似地,T2正在将金额50从帐户2转移到帐户1:

Similarly, T2 is transferring amount 50 from account2 to account1:

account2.transfer(account1, 50);

transfer()方法显然容易死锁,因为两个线程T1和T2试图以相反的顺序获取锁. (线程T1会先尝试获得对account1的锁定,然后再获得account2.而线程T2会先尝试获得对account2的锁定,然后再获得account1的锁定.)

The transfer() method is obviously prone to deadlock as two threads T1 and T2 would be trying to acquire lock in the reverse order. (Thread T1 will try acquiring lock on account1 first and then account2. Whereas thread T2 will try acquiring lock on account2 and then on account1.)

在这种情况下,确保始终保证锁定顺序的最佳方法是什么?

What is the best way (in this case) to ensure locking-order to be guaranteed always?

public class Account {
    private float balance;

    public class Account() {
        balance = 5000f;
    }

    private void credit(float amt) {
        balance += amt;
    }

    // To exclude noise assume the balance will never be negative
    private void debit(float amt) {
        balance -= amt;
    }

    // Deadlock prone as the locking order is not guaranteed
    public void transfer(Account acc2, float amt) {
        synchronized(this) {
            synchronized(acc2) {
                acc2.debit(amt);
                this.credit(amt);
            }
        }
    }
}

推荐答案

您可以自己实现同步块的排序.在创建时为每个帐户创建一个唯一的ID,并以已排序的顺序使用同步:

You can implement ordering of the synchronized blocks yourself. Create a unique id for each account on creation and use synchronized in sorted order:

class Account {

  private float balance;
  private final int id;
  private static AtomicInteger idGen = new AtomicInteger(0);

  public Account() {
    id = idGen.incrementAndGet();
    balance = 5000f;
  }

  private void credit(float amt) {
    balance += amt;
  }

  // To exclude noise assume the balance will never be negative
  private void debit(float amt) {
    balance -= amt;
  }

  // Deadlock prone as the locking order is not guaranteed
  public void transfer(Account acc2, float amt) {
    Account first = this.id > acc2.id ? acc2 : this;
    Account second = this.id > acc2.id ? this : acc2;

    synchronized (first) {
      synchronized (second) {
        acc2.debit(amt);
        this.credit(amt);
      }
    }

  }
}

但是只有当您知道所有要预先锁定的帐户时,这种方法才有用.

But this approach is usable only if you know all the accounts to lock in advance.

我将尝试澄清有关提前了解所有锁的部分.

I will try to clarify the part about knowing all the locks in advance.

在这样一个简单的示例中,很容易收集所有需要的锁,对它们进行排序,然后以正确的顺序锁定它们.当您的代码变得越来越复杂并且您尝试使用抽象来保持代码可读性时,问题就开始了.锁排序概念有点抽象.当您调用一些封装的未知代码(该代码试图获取更多锁或调用其他代码)时,您将无法再确保正确的锁顺序.

In a simple exemaple like this, it is easy to collect all the needed locks, sort them and then lock them in correct order. The problem starts when your code gets more and more complicated and you try to use abstraction to keep the code readable. The lock ordering concept kind of goes aginst abstraction. When you call some encapsulated unknown code (which migth try to acquire more locks or call other code), you can no longer ensure correct lock ordering.

这篇关于如何确保锁定顺序以避免死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆