Один из способов получить взаимоблокировку - неупорядоченные синхронизации. Этот пример блокирует два потока и затем использует вышеупомянутую проверку, чтобы найти их. Все это можно запустить из основного метода.
final Object a = new Object();
final Object b = new Object();
CountDownLatch latch = new CountDownLatch(2);
new Thread(()->{
System.out.println(Thread.currentThread().getId() + " running");
synchronized(a){
try{
latch.countDown();
latch.await();
} catch(InterruptedException e){
return;
}
synchronized(b){
System.out.println("no deadlock");
}
}
}).start();
new Thread(()->{
System.out.println(Thread.currentThread().getId() + " running");
synchronized(b){
try{
latch.countDown();
latch.await();
} catch(InterruptedException e){
return;
}
synchronized(a){
System.out.println("no deadlock");
}
}
}).start();
Добавив небольшую задержку, я могу найти два тупиковых идентификатора потока.
Thread.sleep(100);
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads();
for(long id: threadIds){
System.out.println(id + " deadlocked");
}
Тогда вывод выглядит так:
18 работает
19 работает
19 заблокирован
18 заблокирован
Thread.sleep
можно опустить, но тогда есть условие гонки. Возможно, bean.findDeadlockedThreads();
может произойти, прежде чем возникнет тупик Даже во сне есть состояние гонки, но это невероятно редко. Защелка может сделать это гарантией.
CountDownLatch может быть удален, но тогда потоки будут иногда только тупиковыми. В других случаях один поток завершается до запуска другого.