Что такое deadlock? Это ситуация, когда две конкурирующих программы или нити в программе пытаются использовать общие ресурсы таким образом, что ни одна из них не может продолжить выполнение.
Простейший случай deadlock — это когда нить-1 получает эксклюзивный доступ к ресурсу-1, затем пытается получить доступ к ресурсу-2, который в это время уже эксклюзивно захвачен нитью-2, который в свою очередь пытается получить доступ к ресурсу-1.
Кстати, как можно перевести этот термин на русский язык? Можно найти такие переводы:
взаимоблокировка
тупик
взаимная блокировка
перекрестная блокировка
мертвая точка
смертельный захват
мертвая блокировка
мертвый замок
Ни один из них не кажется особенно удачным, кроме может быть первого, который, по нашему мнению не очень удобно использовать из-за его длины.
Deadlock-и могут возникать и в приложениях, и в ядре операционной системы, и в базах данных. При их возникновении нормальная работа приложения становится невозможна, поэтому необходимо заранее избегать ситуации взаимоблокировки или отлеживать возникшие deadlock-и и применять специальные меры. Например, СУБД могут откатывать одну или несколько транзакций, чтобы дать ход остальным. Но как сделать так, чтобы deadlock-и не возникали в программе? Самый простой способ — выбрать порядок захвата общих ресурсов и придерживаться его во всех возможных участках в программе. Тем не менее, в сложных приложениях это не всегда легко, и важной задачей становится обнаружение ситуаций deadlock-а.
В приложениях на Java взаимоблокировка может возникнуть, когда две нити пытаются захватить мониторы двух объектов в разном порядке. Например, когда одна нить исполняет следующий код:
1: synchronized (lock1) {
2: synchronized (lock2) {
3: // ...
4: }
5: }
а другая:
1: synchronized (lock2) {
2: synchronized (lock1) {
3: // ...
4: }
5: }
Это — потенциальный deadlock. Не факт, что он обязательно возникнет — для этого исполнение этого кода должно произойти таким образом, чтобы нити уже захватив lock1 и lock2 в строке 1, пытались одновременно захватить вторую блокировку (lock2 и lock1, строка 2).
Как обнаружить deadlock в Java программе? К счастью, для этого в JVM есть встроенные средства. Их несколько:
Java Dump, который происходит при нажатии Ctrl-\ на Unix или Ctrl-Break на Windows
Вот пример программы, которая искуственно создает deadlock и обнаруживает его с помощью ThreadMXBean.findMonitorDeadlockedThreads().
import java.lang.management.*;
public class Deadlock {
volatile static boolean[] locked = {false, false};
final static String[] resources = {"resource1", "resource2"};
static class DeadlockMakerThread extends Thread {
int[] order;
DeadlockMakerThread(String name, int[] order) { super(name); this.order = order; }
public void run()
{
// do lock first resource
synchronized( resources[order[0]] )
{
System.out.println(getName() + ": locked resource: " + resources[order[0]]);
locked[order[0]] = true;
// wait till second resource will be locked by another thread
while (!locked[order[1]])
{
try { Thread.sleep(500); }
catch (InterruptedException e) { e.printStackTrace(); }
}
// try to lock second resource (at this time it is already locked by another thread)
synchronized( resources[order[1]] ) {
System.out.println(getName() + ": locked resource: " + resources[order[1]]);
}
}
}
}
public static void showDeadlock() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] ids = threadMXBean.findMonitorDeadlockedThreads();
if (ids != null) {
System.out.print("Deadlocked thread ids: ");
for (long id : ids)
System.out.print(id + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] order1 = {0, 1};
int[] order2 = {1, 0};
new DeadlockMakerThread("Thread1", order1).start();
new DeadlockMakerThread("Thread2", order2).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
showDeadlock();
}
}
Этот же deadlock обнаруживается при нажатии Ctrl-\ или при использовании JStack.
$ java Deadlock &
Thread1: locked resource: resource1
Thread2: locked resource: resource2
Deadlocked thread ids: 8 7
$ jps
14596 Deadlock
$ jstack 14596
...
Found one Java-level deadlock:
=============================
"Thread2":
waiting to lock monitor 0x080a9be8 (object 0xb1e93980, a java.lang.String),
which is held by "Thread1"
"Thread1":
waiting to lock monitor 0x080a9c4c (object 0xb1e939b8, a java.lang.String),
which is held by "Thread2"
Java stack information for the threads listed above:
===================================================
"Thread2":
at Deadlock$DeadlockMakerThread.run(Deadlock.java:28)
- waiting to lock <0xb1e93980> (a java.lang.String)
- locked <0xb1e939b8> (a java.lang.String)
"Thread1":
at Deadlock$DeadlockMakerThread.run(Deadlock.java:28)
- waiting to lock <0xb1e939b8> (a java.lang.String)
- locked <0xb1e93980> (a java.lang.String)
Found 1 deadlock.
...
А вот что показывает jconsole:

JStack и JConsole более удобны тем, что их можно использовать для работы с уже запущенным приложением. Это особенно полезно при отладке проблем в критической программе (например сервере приложений). К ThreadMXBean также можно получить доступ удаленно, что на самом деле используется в JConsole.
Это не единственная ситуация, когда может возникнуть deadlock. Захват монитора может происходить и в других ситуациях, например внутри блока synchronized при выходе из Object.wait(), при использовании JNI MonitorEnter, и в других случаях. J2SE 5.0 (Tiger) обнаруживает только проблемы связанные с мониторами. А в Java SE 6.0 (Mustang) уже добавлена возможность обнаруживать deadlock-и связанные с новыми блокировками — java.util.concurrent.locks.
Ссылки по теме:
Н.Х.
опубликовал vmrobot ( апр 21 2006, 02:04:33 PM MSD ) Permalink Комментарии [3]


опубликовал Dmitry Апрель 21, 2006 at 02:57 PM MSD #
опубликовал ekr Июнь 25, 2006 at 04:39 PM MSD #
опубликовал somebody Июнь 06, 2007 at 01:09 PM MSD #