Имитация взаимного блокирования потоков
Пытаюсь разобраться с потоками, хочу заставить потоки блокировать друг друга. Я так понимаю потоки должны циклично задействовать или ожидать друг друга, но не понимаю как это реализовать. Вот мой код.
packagedeadlock;
class A
{
synchronized void foo(B b)
{
String name=Thread.currentThread().getName();
System.out.println(name +" вошел а A.foo()");
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
System.out.println("A прерван");
}
System.out.println(name+" пытается вызвать B.last()");
b.last();
}
void last()
{
System.out.println(" внутри A.last()");
}
}
class B
{
synchronized void bar(A a)
{
String name=Thread.currentThread().getName();
System.out.println(name +" вошел в B.bar()");
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
System.out.println("B прервано");
}
System.out.println(name+" пытается вызвать A.last()");
a.last();
}
void last()
{
System.out.println(" внутри B.last()");
}
}
public class Deadlock implements Runnable
{
A a=new A();
B b=new B();
Deadlock()
{
Thread.currentThread().setName("MainThread");
Thread t=new Thread(this, "RacingThread");
t.start();
a.foo(b);
System.out.println("Назад в главный поток");
}
public void run()
{
b.bar(a);
System.out.println("Назад в другой поток");
}
public static void main(String[] args)
{
new Deadlock();
}
}
Ответы (1 шт):
Автор решения: Roman Konoval
→ Ссылка
Чтобы добиться дедлока нужно:
- два ресурса
- два потока
- чтобы потоки одновременно захватили по одному разному ресурсу
- чтобы потоки попытались захватить второй ресурс
В этом случае они будут ждать друг друга бесконечно.
Реализовать можно так:
public class T {
static class A {
private CyclicBarrier barrier;
A(CyclicBarrier barrier) {
this.barrier = barrier;
}
synchronized void f(A other, boolean nested) throws InterruptedException, BrokenBarrierException {
// в этой точке монитор объекта this захвачен
if (!nested) {
barrier.await(); // ждем пока второй поток дойдет до этой же точки
other.f(this, true); // пробуем захватить монитор объекта other
}
}
}
public void lockInOrder(A first, A second) {
try {
first.f(second, false);
} catch (InterruptedException | BrokenBarrierException e) {
throw new RuntimeException(e);
}
}
@Test
public void deadlock() throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(2);
A a1 = new A(barrier);
A a2 = new A(barrier);
Thread t1 = new Thread(() -> {
lockInOrder(a1, a2);
});
Thread t2 = new Thread(() -> {
lockInOrder(a2, a1);
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
То, что потоки в дедлоке (т.е. ждут мониторы захваченные друг другом) видно с помощью jstack. Обратите внимание на строки с locked и waiting for lock:
"Thread-2" #21 prio=5 os_prio=31 tid=0x00007fe8758f4000 nid=0x6303 waiting for monitor entry [0x000070000b47e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at so.T$A.f(T.java:16)
- waiting to lock <0x0000000718cf6698> (a so.T$A)
at so.T$A.f(T.java:18)
- locked <0x0000000718cf66a8> (a so.T$A)
at so.T.lockInOrder(T.java:25)
at so.T.lambda$1(T.java:41)
at so.T$$Lambda$253/103536485.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-1" #20 prio=5 os_prio=31 tid=0x00007fe885a26000 nid=0x9a03 waiting for monitor entry [0x000070000b37b000]
java.lang.Thread.State: BLOCKED (on object monitor)
at so.T$A.f(T.java:16)
- waiting to lock <0x0000000718cf66a8> (a so.T$A)
at so.T$A.f(T.java:18)
- locked <0x0000000718cf6698> (a so.T$A)
at so.T.lockInOrder(T.java:25)
at so.T.lambda$0(T.java:38)
at so.T$$Lambda$252/572191680.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)