java 死锁的检测与修复_调查死锁–第4部分:修复代码

java 死锁的检测与修复

在这个简短的博客系列的最后BadTransferOperation中,我一直在讨论分析死锁,我将修复BadTransferOperation代码。 如果您已经看过本系列的其他博客 ,那么您会知道,为了达到这一点,我创建了死锁演示代码,展示了如何掌握线程转储,然后分析了线程转储,找出发生僵局的位置和方式。 为了节省空间,下面的讨论同时引用了本系列第1部分中AccountDeadlockDemo类,其中包含完整的代码清单。

教科书中有关死锁的描述通常是这样的:“线程A将获得对象1的锁定,并等待对象2的锁定,而线程B将获得对象2的锁定,同时等待对象1的锁定”。 我以前的博客中显示的堆积,并在下面突出显示,是一个现实世界中的死锁,其他线程,锁和对象陷入了直接,简单,理论上的死锁情况。

Found one Java-level deadlock:
=============================
'Thread-21':
  waiting to lock monitor 7f97118bd560 (object 7f3366f58, a threads.deadlock.Account),
  which is held by 'Thread-20'
'Thread-20':
  waiting to lock monitor 7f97118bc108 (object 7f3366e98, a threads.deadlock.Account),
  which is held by 'Thread-4'
'Thread-4':
  waiting to lock monitor 7f9711834360 (object 7f3366e80, a threads.deadlock.Account),
  which is held by 'Thread-7'
'Thread-7':
  waiting to lock monitor 7f97118b9708 (object 7f3366eb0, a threads.deadlock.Account),
  which is held by 'Thread-11'
'Thread-11':
  waiting to lock monitor 7f97118bd560 (object 7f3366f58, a threads.deadlock.Account),
  which is held by 'Thread-20'


如果将上面的文本和图像与以下代码相关联,则可以看到Thread-20已锁定其fromAccount对象( fromAccount ),正在等待锁定其toAccount对象(e98)

private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {

      synchronized (fromAccount) {
        synchronized (toAccount) {
          fromAccount.withdraw(transferAmount);
          toAccount.deposit(transferAmount);
        }
      }
    }

不幸的是,由于时序问题, Thread-20无法获得对对象e98的锁定,因为它正在等待Thread-4释放对该对象的锁定。 Thread-4无法释放锁,因为它正在等待Thread-7Thread-7正在等待Thread-11Thread-11正在等待Thread-20释放对对象f58的锁。 这个现实世界的僵局只是教科书描述的一个更复杂的版本。

这段代码的问题是,从下面的代码片段中,您可以看到我正在从Accounts数组中随机选择两个Account对象作为fromAccounttoAccount并将它们锁定。 由于fromAccounttoAccount可以引用accounts数组中的任何对象,这意味着它们以随机顺序被锁定。

Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));
 Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));

因此, 解决方法是对Account对象的锁定方式施加顺序,并且只要顺序一致,任何顺序都可以执行。

private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {

      if (fromAccount.getNumber() > toAccount.getNumber()) {

        synchronized (fromAccount) {
          synchronized (toAccount) {
            fromAccount.withdraw(transferAmount);
            toAccount.deposit(transferAmount);
          }
        }
      } else {

        synchronized (toAccount) {
          synchronized (fromAccount) {
            fromAccount.withdraw(transferAmount);
            toAccount.deposit(transferAmount);
          }
        }
      }
    }

上面的代码显示了此修复程序。 在此代码中,我使用帐号来确保首先锁定具有最高帐号的Account对象,这样就不会出现以上的死锁情况。

下面的代码是此修复程序的完整列表:

public class AvoidsDeadlockDemo {

  private static final int NUM_ACCOUNTS = 10;
  private static final int NUM_THREADS = 20;
  private static final int NUM_ITERATIONS = 100000;
  private static final int MAX_COLUMNS = 60;

  static final Random rnd = new Random();

  List<Account> accounts = new ArrayList<Account>();

  public static void main(String args[]) {

    AvoidsDeadlockDemo demo = new AvoidsDeadlockDemo();
    demo.setUp();
    demo.run();
  }

  void setUp() {

    for (int i = 0; i < NUM_ACCOUNTS; i++) {
      Account account = new Account(i, rnd.nextInt(1000));
      accounts.add(account);
    }
  }

  void run() {

    for (int i = 0; i < NUM_THREADS; i++) {
      new BadTransferOperation(i).start();
    }
  }

  class BadTransferOperation extends Thread {

    int threadNum;

    BadTransferOperation(int threadNum) {
      this.threadNum = threadNum;
    }

    @Override
    public void run() {

      for (int i = 0; i < NUM_ITERATIONS; i++) {

        Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));
        Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));
        int amount = rnd.nextInt(1000);

        if (!toAccount.equals(fromAccount)) {
          try {
            transfer(fromAccount, toAccount, amount);
            System.out.print(".");
          } catch (OverdrawnException e) {
            System.out.print("-");
          }

          printNewLine(i);
        }
      }
      System.out.println("Thread Complete: " + threadNum);
    }

    private void printNewLine(int columnNumber) {

      if (columnNumber % MAX_COLUMNS == 0) {
        System.out.print("\n");
      }
    }

    /**
     * This is the crucial point here. The idea is that to avoid deadlock you need to ensure that threads can't try
     * to lock the same two accounts in the same order
     */
    private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {

      if (fromAccount.getNumber() > toAccount.getNumber()) {

        synchronized (fromAccount) {
          synchronized (toAccount) {
            fromAccount.withdraw(transferAmount);
            toAccount.deposit(transferAmount);
          }
        }
      } else {

        synchronized (toAccount) {
          synchronized (fromAccount) {
            fromAccount.withdraw(transferAmount);
            toAccount.deposit(transferAmount);
          }
        }
      }
    }
  }
}

在我的示例代码,死锁的发生是因为时机问题,嵌套的synchronized在我的关键字BadTransferOperation类。 在此代码中, synchronized关键字位于相邻的行上; 但是,最后一点,值得注意的是, synchronized关键字在代码中的什么位置都没有关系(它们不必相邻)。 只要用同一线程锁定两个(或更多)不同的监视对象,就会发生排序和死锁。

有关更多信息,请参阅本系列中的其他博客

该系列以及其他博客的所有源代码都可以在Github上找到,网址为git://github.com/roghughe/captaindebug.git

参考: 调查死锁–第4部分:来自Captain Debug博客博客的JCG合作伙伴 Roger Hughes 修复代码

翻译自: https://www.javacodegeeks.com/2012/11/investigating-deadlocks-part-4-fixing-the-code.html

java 死锁的检测与修复

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值