如何理解导致死锁的嵌套监视器?

时间:2017-08-30 13:46:50

标签: java multithreading concurrency synchronization monitor

来自编程语言语用学,由Scott

  

等待时,保持对外部监视器的排除   内部的可能导致信号线程死锁,释放   那些外部监视器可能导致类似(如果有点更微妙)   死锁。当等待线程唤醒时,它必须重新获取排除   在内部和外部监视器上。最内层的监视器当然是   可用,因为匹配信号发生在那里,但有   一般无法确保不相关的线程不会忙   外部监视器。此外,其中一个线程可能需要访问   内部监视器,以完成其工作并释放外部   监视(一个或多个)。如果我们坚持认为被唤醒的线程是第一个运行的   在信号之后的内部监视器中,将导致死锁。一   避免这个问题的方法是安排互相排斥   程序的所有监视器。这个解决方案严重限制   多处理器实现中的并发性,但可能是可接受的   在单处理器上。

我以粗体突出显示的两个案例如何导致死锁?

  • 在内部等待时保持对外部监视器的排除
  • 释放那些外部监视器

我很难理解文本,尤其是第二种情况。 感谢任何帮助以更清晰的方式解释它们。

感谢。

2 个答案:

答案 0 :(得分:1)

在内部监视器上等待时保持对外部监视器的排除当进程在内部监视器上等待时,它可以将其排除在它正在维护的所有外部监视器上。当另一个进程想要访问其中一个外部监视器时,这可能会导致死锁,但是可以访问其自身的内部监视器,从而造成死锁。

释放那些外部监视器:想象一个线程在等待内部监视器并释放他的外部以确保其他人可以在进程等待时获得对它们的访问权限。现在当线程唤醒时(=内部监视器可以再次访问它),它需要获取在等待时释放的外部监视器。这就出现了这个问题:"但是通常没有办法确保不相关的线程不会在外部监视器中忙碌"

答案 1 :(得分:1)

参见案例,代码如下:

new Thread(new Runnable() {     //thread 1
        @Override
        public void run() {
            synchronized (outer){
                synchronized (inner){
                    try {
                        outer.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }).start();

    Thread.sleep(1000);

    new Thread(new Runnable() {   //thread2
        @Override
        public void run() {
            synchronized (outer){
                System.out.println("get outer");
                synchronized (inner){
                    System.out.println("get inner");
                }
            }
        }
    }).start();

thread1将释放外部监视器,但仍然保持内部监视器,当thread2运行时,它将成功获取外部监视器,但无法获取内部监视器。这将发生死锁。

另见另一案例: 将outer.wait();更改为inner.wait();,这仍然会发生死锁。 因为thread1将保持外部监视器,thread2将不会获取它。