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

How to understand nested monitors leading to deadlocks?

来自编程语言语用学,作者:Scott

While maintaining exclusion on outer monitor(s) when waiting in an inner one may lead to deadlock with a signaling thread, releasing those outer monitors may lead to similar (if a bit more subtle) deadlocks. When a waiting thread awakens it must reacquire exclusion on both inner and outer monitors. The innermost monitor is of course available, because the matching signal happened there, but there is in general no way to ensure that unrelated threads will not be busy in the outer monitor(s). Moreover one of those threads may need access to the inner monitor in order to complete its work and release the outer monitor(s). If we insist that the awakened thread be the first to run in the inner monitor after the signal, then deadlock will result. One way to avoid this problem is to arrange for mutual exclusion across all the monitors of a program. This solution severely limits concurrency in multiprocessor implementations, but may be acceptable on a uniprocessor.

我用粗体标出的两种情况是怎么导致死锁的?

我看不懂文字,尤其是第二种情况。 如果能以更清晰的方式解释它们,我们将不胜感激。

谢谢。

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

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

看案例,代码如下:

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会释放outer monitor,但仍持有inner monitor,当thread2 运行时,会成功获取outer monitor,但无法获取inner monitor.This will发生死锁。

再看另一种情况: 将 outer.wait(); 更改为 inner.wait();,这仍然会发生死锁。 因为 thread1 将持有外部监视器,而 thread2 不会获取它。