Интересной особенностью взаимоблокировки в Java является то, что участвующие в ней потоки невозможно остановить, то есть если в приложении возникает взаимоблокровка и приложение каким-либо способом смогло её обнаружить, оно ничего не сможет с этим поделать, хотя возможно было бы, например, завершить один из блокированных потоков и тем самым дать возможность всем остальным продолжить работу.
Проблема в том, что невозможно завершить поток, который пытается получить блокировку на объект, входя в synchronized метод или блок. Однако с появлением в Java новых способов синхронизации помимо synchronized методов и блоков, а именно пакета java.util.concurrent, ситуация улучшилась, так-как с помощью Thread.stop() возможно завершить выполнение потока, который пытается получить блокировку используя java.util.concurrent.Lock.lock(), а если для получения блокировки используется java.util.concurrent.Lock.lockInterruptibly() то можно просто прервать это метод, вызвав Thread.interrupt(), и при этом все потоки останутся живы!
Рассмотрим небольшой пример, в котором Java программа обнаруживает и ликвидирует взаимоблокировку используя Thread.stop() (и заодно проверим, что monitoring API в Java 6.0 умеет находить взаимоблокировки созданные с помощью java.util.concurrent.Lock). Применяя Thread.stop(), нельзя забывать, что это метод не рекомендуется использовать и прежде чем его вызывать стоит хорошо обдумать все последствия.
// Deadlock.javaЗапускаем:
import java.lang.management.*;
import java.util.concurrent.locks.*;
public class Deadlock {
// два ресурса для создания взаимоблокировки:
final static Object resource1 = new Object();
static boolean fisrtResourceLocked;
final static ReentrantLock resource2 = new ReentrantLock();
static boolean secondResourceLocked;
// два потока, которые захватывают ресурсы в разной последовательности:
// DeadlockMakerThread1 сначала захватывает resource1, потом пытается захватить resource2
static class DeadlockMakerThread1 extends Thread {
DeadlockMakerThread1(String name) {
super(name);
}
public void run() {
synchronized (resource1) {
System.out.println(getName() + ": ресурс захвачен: " + resource1);
fisrtResourceLocked = true;
while (!secondResourceLocked) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + ": пытаюсь захватить " + resource2);
resource2.lock();
System.out.println(getName() + ": ресурс захвачен: " + resource2);
}
}
}
// DeadlockMakerThread2 сначала захватывает resource2, потом пытается захватить resource1
static class DeadlockMakerThread2 extends Thread {
DeadlockMakerThread2(String name) {
super(name);
}
public void run() {
resource2.lock();
System.out.println(getName() + ": ресурс захвачен: " + resource2);
secondResourceLocked = true;
while (!fisrtResourceLocked) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + ": пытаюсь захватить " + resource1);
synchronized (resource1) {
System.out.println(getName() + ": ресурс захвачен: " + resource1);
}
}
}
// пытаемся обнаружить взаимоблокировку с помощью метода ThreadMXBean.findDeadlockedThreads()
public static void showDeadlock() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] ids = threadMXBean.findDeadlockedThreads();
if (ids != null) {
System.out.print("Обнаружена взаимоблокировка, идентификаторы потоков: ");
for (long id : ids)
System.out.print(id + " ");
} else {
System.out.println("Взаимоблокировка не обнаружена");
}
System.out.println();
}
public static void main(String[] args) {
Thread thread1 = new DeadlockMakerThread1("Поток1");
Thread thread2 = new DeadlockMakerThread2("Поток2");
thread1.start();
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
showDeadlock();
// остановливаем поток, который блокирован вызовом ReentrantLock.lock()
System.out.println("Останавливаем поток: " + thread1.getName());
thread1.stop();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
showDeadlock();
System.exit(0);
}
}
Как видно из полученного результата Поток2 после остановки Потока1 смог продолжить выполнение и взаимоблокировка исчезла! В этом примере даже метод Thread.getState() вернёт разные значения для потоков участвующих в блокировке: для Потока1, вызывающего Lock.lock()—ThreadState.WAITING, а для Потока2, который пытается войти в synchronized блок—ThreadState.BLOCKED. То есть с точки зрения виртуальной машины потоки находятся в разных состояниях, это связано с тем, что в этих двух случаях используются разные механизмы синхронизации. В частности реализация java.util.concurrent.Lock.lock() напрямую использует возможности синхронизации, предоставляемые операционной системой (я думаю это ещё одна причина узнать больше о пакете java.util.concurrent и, может быть, начать им пользоваться).
java Deadlock
Поток1: ресурс захвачен: java.lang.Object@de6ced
Поток2: ресурс захвачен: java.util.concurrent.locks.ReentrantLock@c17164[Locked by thread Поток2]
Поток2: пытаюсь захватить java.lang.Object@de6ced
Поток1: пытаюсь захватить java.util.concurrent.locks.ReentrantLock@c17164[Locked by thread Поток2]
Обнаружена взаимоблокировка, идентификаторы потоков: 9 8
Останавливаем поток: Поток1
Поток2: ресурс захвачен: java.lang.Object@de6ced
Взаимоблокировка не обнаружена
Возможно, кроме этого достаточно грубого способа разрешения взаимоблокировки в следующем релизе Java появится более мощная поддержка борьбы с взаимоблокировками, например, в потоках, участвующих во взаимной блокировке могло бы быть выброшено исключение и проложение, перехватив его, могло бы разрешить возникшую проблему(возможность такого поведения уже упоминается в описании java.util.concurrent.Lock.lock()).
опубликовал vmrobot ( окт 06 2006, 02:34:00 PM MSD ) Permalink Комментарии [1]

опубликовал Kirill Shirokov Январь 23, 2007 at 07:05 PM MSK #