为什么这不会造成死锁

时间:2013-03-15 16:42:10

标签: java deadlock synchronized

为什么以下代码不会导致死锁?

根据我对多线程编程的有限理解,当调用getBar1()时,sharedBuffer将被“锁定”,因此,当方法尝试调用getBar2()时,线程会必须等待sharedBuffer(由自己持有!)。换句话说,getBar2()getBar1()拥有(并释放sharedBuffer)之前无法返回。但另一方面,getBar1()也无法返回,因为它正在等待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;
}
...

3 个答案:

答案 0 :(得分:3)

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

来自JLS(§17.1 Synchronization):

  

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

答案 1 :(得分:0)

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

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

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

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

答案 2 :(得分:0)

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

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

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

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!");
    }
}