1

为什么下面这段代码不会导致死锁?

根据我对多线程编程的有限理解,当getBar1()被调用时,sharedBuffer将被“锁定”,因此,当方法尝试调用getBar2()时,线程将不得不等待sharedBuffer(它自己持有!)。换句话说,直到has (和 release )getBar2()才能返回。但另一方面,也不能返回,因为它正在等待返回。getBar1()sharedBuffergetBar1()getBar2()

==> 死锁。(但实际上并非如此,这就是我感到困惑的原因)

...
Foo sharedBuffer = new Foo();

Bar1 getBar1()
{
     Bar1 bar1;
     synchronized (sharedBuffer)
     {
            bar1 = sharedBuffer.getBar1();
            if (bar1 == null)
                bar1 = new Bar1(sharedBuffer, getBat2());
            sharedBuffer.setBar1(bar1);
     }
     return bar1;
}

Bar2 getBar2()
{
    Bar2 bar2;
    synchronized (sharedBuffer)
    {
        bar2 = sharedBuffer.getBar2();
        if (bar2 == null)
            bar2 = new Bar2();
    }
    return bar2;
}
...
4

3 回答 3

3

Java 的监视器是递归的,这意味着同一个线程可以多次获取相同的锁。

从 JLS(§17.1 同步):

一个线程 t 可能会多次锁定一个特定的监视器;每次解锁都会反转一次锁定操作的效果。

于 2013-03-15T16:44:36.820 回答
0

它不会死锁,因为您实际上只有一个锁。在这两个函数中,您都锁定了 sharedBuffer。当第一个线程调用 getBar1() 时,它锁定 sharedBuffer。当同一个线程调用 getBar2() 时,它会命中同步块并且已经拥有锁,所以它只是进入锁。

如果要导致死锁,请使用两个不同的值来锁定。然后,只有当时间安排正确时,您才会看到它。如果要强制执行死锁,请确保第一个线程的睡眠时间足够长,以便第二个线程获得锁。

这是一些会死锁的代码......(未经测试,prolly 有错别字)。这应该有效,因为不同的线程拥有锁而不是想要锁的线程。

public class Deadlock extends Thread
{
    private Deadlock other;
    private String name;

    public static void main(String[] args)
    {
        Deadlock one = new Deadlock("one");
        Deadlock two = new Deadlock("two");
        one.setOther(two);
        two.setOther(one);
        one.start();
        two.start();
    }

    public setOther(Deadlock other){ this.other = other; }

    public void run() {
       deadlock();
    }

    public synchronized deadlock() {
         System.out.println("thread " + this.name + " entering this.deadlock()");
         sleep(1000); // adjust as needed to guarantee deadlock
         System.out.println("thread " + this.name + " calling other.deadlock()");
         other.deadlock(this.name);
         System.out.println(name + " - deadlock avoided!");
    }
}
于 2013-03-15T16:47:46.667 回答
0

当并发操作试图以不同的顺序锁定两个或更多资源时,就会发生死锁,并且它们都被卡住等待被另一个锁定的资源。

例如,线程 T1 和 T2 在资源 R1 和 R2 上同步:

  • T1 在 R1 上同步。
  • 调度程序决定 T2 应该运行
  • T2 在 R2 上同步。
  • T2 尝试在 R1 上同步;它被迫等到 T1 放弃锁定。
  • 调度器看到 T2 不能继续运行,所以允许 T1 运行
  • T1 尝试在 R2 上同步;它被迫等到 T2 放弃锁定。
  • 两个线程都无法继续

您在这里所做的是基本同步,一次只允许一个对象访问sharedBuffer

于 2013-03-15T16:44:50.437 回答