Не вдаваясь в подробности, потоки могут находиться в одном из нескольких состояний, как показано на диаграмме состояний UML ниже… 
… И взаимоблокировка — это все, что связано с состоянием BLOCKED, которое в документации API определяется как «поток, который заблокирован в ожидании блокировки монитора».
Итак, что такое тупик? Проще говоря, при наличии двух потоков A и B возникает тупик, когда поток A блокирует, потому что он ожидает, пока поток B освободит блокировку монитора, и поток B блокирует, потому что он ожидает, что поток A снимет такую же блокировку монитора.
Тем не менее, все может быть более сложным, чем это в том, что тупик может содержать целую кучу потоков. Например, поток A блокирует, потому что он ожидает поток B, поток B блокирует, потому что он ожидает поток C, поток C блокирует, потому что он ожидает блоки D, D, потому что он ждет блоки E, E, потому что он ждет F и F блоки, потому что он ждет А.
 Хитрость заключается в том, чтобы выяснить, какие потоки заблокированы и почему, и это делается путем получения дампа потоков из вашего приложения.  Дамп потока — это просто отчет о снимке, показывающий состояние всех потоков вашего приложения в данный момент времени.  Есть несколько инструментов и методов, которые помогут вам получить дамп потока, включая jVisualVM , jstack и команду unix kill ;  однако перед получением и интерпретацией дампа потока мне понадобится код, который создаст тупик 
  Сценарий, который я выбрал для этого, представляет собой простой банковский перевод.  Идея состоит в том, что существует программа переноса баланса, которая случайным образом переводит различные суммы между разными учетными записями, используя несколько потоков.  В этой программе банковский счет представлен с использованием следующего, очень упрощенного класса Account : 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | publicclassAccount {  privatefinalintnumber;  privateintbalance;  publicAccount(intnumber, intopeningBalance) {    this.number = number;    this.balance = openingBalance;  }  publicvoidwithdraw(intamount) throwsOverdrawnException {    if(amount > balance) {      thrownewOverdrawnException();    }    balance -= amount;  }  publicvoiddeposit(intamount) {    balance += amount;  }  publicintgetNumber() {    returnnumber;  }  publicintgetBalance() {    returnbalance;  }} | 
  Приведенный выше класс моделирует банковский счет с атрибутами номера счета и баланса, а также такими операциями, как deposit(...) и withdraw(...) .  withdraw(...) вызовет простое проверенное исключение OverdrawnException , если сумма вывода превышает доступный остаток. 
  Остальные классы в примере кода — это DeadlockDemo и его вложенный класс BadTransferOperation . 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | publicclassDeadlockDemo {  privatestaticfinalintNUM_ACCOUNTS = 10;  privatestaticfinalintNUM_THREADS = 20;  privatestaticfinalintNUM_ITERATIONS = 100000;  privatestaticfinalintMAX_COLUMNS = 60;  staticfinalRandom rnd = newRandom();  List<Account> accounts = newArrayList<Account>();  publicstaticvoidmain(String args[]) {    DeadlockDemo demo = newDeadlockDemo();    demo.setUp();    demo.run();  }  voidsetUp() {    for(inti = 0; i < NUM_ACCOUNTS; i++) {      Account account = newAccount(i, rnd.nextInt(1000));      accounts.add(account);    }  }  voidrun() {    for(inti = 0; i < NUM_THREADS; i++) {      newBadTransferOperation(i).start();    }  }  classBadTransferOperation extendsThread {    intthreadNum;    BadTransferOperation(intthreadNum) {      this.threadNum = threadNum;    }    @Override    publicvoidrun() {      for(inti = 0; i < NUM_ITERATIONS; i++) {        Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));        Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));        intamount = 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);    }    privatevoidprintNewLine(intcolumnNumber) {      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.     */    privatevoidtransfer(Account fromAccount, Account toAccount, inttransferAmount) throwsOverdrawnException {      synchronized(fromAccount) {        synchronized(toAccount) {          fromAccount.withdraw(transferAmount);          toAccount.deposit(transferAmount);        }      }    }  }} | 
  DeadlockDemo предоставляет каркас приложения, который создает тупик.  У него две простые задачи: setup() и run() .  setup() создает 10 учетных записей, инициализируя их номером счета и случайным начальным балансом.  run() создает 20 экземпляров вложенного класса BadTransferOperation , который просто расширяет Thread и запускает их выполнение.  Обратите внимание, что значения, используемые для количества потоков и учетных записей, являются абсолютно произвольными. 
  BadTransferOperation — то, где все действие имеет место.  Его метод run() зацикливает 10000 раз случайным образом, выбирая две учетные записи из списка accounts и передавая случайную сумму от 0 до 1000 от одной к другой.  Если в fromAccount недостаточно средств, возникает исключение и на экране fromAccount знак «-».  Если все прошло хорошо и передача прошла успешно, то «.»  печатается на экране. 
  Суть вопроса — метод transfer(Account fromAccount, Account toAccount, int transferAmount) содержащий код синхронизации FAULTY : 
| 1 2 3 4 5 6 | synchronized(fromAccount) {        synchronized(toAccount) {          fromAccount.withdraw(transferAmount);          toAccount.deposit(transferAmount);        }      } | 
  Этот код сначала блокирует fromAccount , а затем toAccount прежде чем переводить деньги и впоследствии toAccount обе блокировки. 
  Учитывая два потока A и B и учетные записи 1 и 2, тогда возникнут проблемы, когда поток A заблокирует свой fromAccount , номер 1, и попытается заблокировать свой toAccount , который является номером счета 2. Одновременно поток B блокирует свой fromAccount , номер 2, и пытается заблокировать свой toAccount , который является номером счета 1. Следовательно, поток A заблокирован в потоке B, а поток B заблокирован в потоке A — тупик. 
Если вы запустите это приложение, вы получите вывод, который выглядит примерно так:
… поскольку программа внезапно останавливается.
Теперь у меня есть заблокированное приложение, мой следующий блог фактически получит дамп потока и рассмотрит, что все это значит.
Ссылка: Исследование тупиков — часть 1 от нашего партнера по JCG Роджера Хьюза в блоге Captain Debug’s Blog .

