windows10僵局_研究僵局–第1部分

windows10僵局

windows10僵局

我敢肯定我们都去过那里:太晚了,您饿了,您的服务器已挂起,或者您的应用程序正在以蜗牛的速度运行,并且有人喘着粗气要您解决问题,然后再去解决。 您的应用程序意外挂起的可能原因之一是称为死锁的线程问题。

无需赘述,线程可以处于多种状态之一,如下面的UML状态图所示……

…并且死锁与BLOCKED状态有关,API文档将其定义为“一个等待监视器锁定而被阻塞的线程”。

那么,什么是僵局? 简而言之,在给定两个线程A和B的情况下,当线程A因为等待线程B释放监视器锁定而阻塞而线程B因等待线程A释放相同的监视器锁定而阻塞时,就会发生死锁。

但是,事情可能比这更复杂,因为死锁可能包含一堆线程。 例如,线程A因为正在等待线程B而阻塞,线程B因为正在等待线程C而阻塞,线程C由于正在等待线程D而阻塞,所以D线程因为正在等待E,E而阻塞,因为它正在等待F和F阻塞,因为它正在等待A。

诀窍是找出哪些线程被阻塞以及为什么被阻塞,这是通过从应用程序中获取线程转储来完成的。 线程转储只是快照报告,显示给定时间点所有应用程序线程的状态。 有几种工具和技术可用来帮助您掌握线程转储,包括jVisualVMjstack和unix kill命令。 但是,在获取和解释线程转储之前,我需要一些代码来创建死锁

我为此选择的方案是简单的银行帐户转帐之一。 这个想法是,有一个余额转移程序正在运行,该程序使用一堆线程在不同帐户之间随机转移各种金额。 在此程序中,使用以下非常简单的Account类来表示银行帐户:

public class Account {

  private final int number;

  private int balance;

  public Account(int number, int openingBalance) {
    this.number = number;
    this.balance = openingBalance;
  }

  public void withdraw(int amount) throws OverdrawnException {

    if (amount > balance) {
      throw new OverdrawnException();
    }

    balance -= amount;
  }

  public void deposit(int amount) {

    balance += amount;
  }

  public int getNumber() {
    return number;
  }

  public int getBalance() {
    return balance;
  }
}

上面的类对具有帐户号和余额属性以及诸如deposit(...)withdraw(...)类的操作的银行帐户进行建模。 如果要提取的金额大于可用余额,则withdraw(...)将引发一个简单的已检查异常OverdrawnException

示例代码中其余的类是DeadlockDemo及其嵌套类BadTransferOperation

public class DeadlockDemo {

  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[]) {

    DeadlockDemo demo = new DeadlockDemo();
    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);
        }
      }
      // This will never get to here...
      System.out.println("Thread Complete: " + threadNum);
    }

    private void printNewLine(int columnNumber) {

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

    /**
     * The clue to spotting deadlocks is in the nested locking - synchronized keywords. Note that the locks DON'T
     * have to be next to each other to be nested.
     */
    private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {

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

DeadlockDemo提供了创建DeadlockDemo的应用程序框架。 它有两个简单的任务: setup()run()setup()创建10个帐户,并使用一个帐号和一个随机的期初余额对其进行初始化。 run()创建嵌套类BadTransferOperation 20个实例,该实例仅扩展Thread并使它们开始运行。 请注意,用于线程数和帐户数的值完全是任意的。

BadTransferOperation是所有动作发生的地方。 它的run()方法循环执行10000次,从accounts列表中随机选择两个帐户,然后将0到1000之间的随机数从一个accounts转移到另一个accounts 。 如果fromAccount中的资金不足,则会引发异常,并在屏幕上显示“-”。 如果一切顺利,并且传输成功,则为“。”。 在屏幕上打印。

问题的核心是包含FAULTY同步代码的方法transfer(Account fromAccount, Account toAccount, int transferAmount)

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

此代码首先锁定fromAccount ,然后toAccount转移现金,随后释放这两个锁定前。

给定两个线程A和B以及帐户1和2,那么当线程A锁定其编号为1的fromAccount并尝试将其锁定为帐户2的toAccount ,就会出现问题。同时,线程B锁定其fromAccount ,编号2和尝试锁定其toAccount ,即帐户号1。因此,线程A在线程B上被toAccount ,线程B在线程A上被阻塞–死锁。

如果运行此应用程序,则将获得一些类似于以下内容的输出:

…程序突然停止。

现在,我有一个死锁的应用程序,我的下一个博客实际上将掌握线程转储,并了解它的全部含义。

参考: Captain Debug's Blog博客中的调查死锁–第1部分,来自我们的JCG合作伙伴Roger Hughes。

翻译自: https://www.javacodegeeks.com/2012/10/investigating-deadlocks-part-1.html

windows10僵局

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值