对象上的Java同步。为什么';这不是僵局吗?

对象上的Java同步。为什么';这不是僵局吗?,java,multithreading,deadlock,synchronized,Java,Multithreading,Deadlock,Synchronized,下面的构造可以正常工作并执行我想要的操作,但我想了解它为什么不会死锁 下面的示例确保用户在继续操作之前已在弹出的JOptionPane框中单击是或否 package waitexample; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; public class WaitExample { public static void main(String[] args) throws Interru

下面的构造可以正常工作并执行我想要的操作,但我想了解它为什么不会死锁

下面的示例确保用户在继续操作之前已在弹出的
JOptionPane
框中单击是或否

package waitexample;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class WaitExample {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("starting");
        Object myWaiter = new Object();

        SwingUtilities.invokeLater(() -> {
            System.out.println("invoked");            
            JOptionPane.showConfirmDialog(null, "Message", "Title", JOptionPane.YES_NO_OPTION);
            synchronized (myWaiter) {
                System.out.println("calling notify");
                myWaiter.notify();
                System.out.println("notified");
            }
        });

        synchronized (myWaiter) {
            System.out.println("waiting");
            myWaiter.wait();
            System.out.println("done waiting");
        }

        System.out.println("ending main()");
    }
}
但是,考虑到以下输出,看起来我正在使用不同的线程同时输入
synchronized(mywater)
块:

starting
waiting
invoked
calling notify
notified
done waiting
ending main()

为什么不会出现死锁?

如果等待的线程在持有锁时处于休眠状态,则会出现死锁。但事实并非如此,尤其是第二段:

使当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法。换句话说,此方法的行为完全类似于它只执行调用等待(0)

当前线程必须拥有此对象的监视器。线程释放此监视器的所有权并等待,直到另一个线程通过调用notify方法或notifyAll方法通知等待此对象监视器的线程唤醒。然后线程等待,直到它可以重新获得监视器的所有权并恢复执行

与单参数版本一样,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:

synchronized(obj){
而()
obj.wait();
…//执行适合条件的操作
}
此方法只能由作为此对象监视器所有者的线程调用。有关线程成为监视器所有者的方式的描述,请参见notify方法

当主线程进入wait方法时,它会释放在进入synchronized块时获取的锁,因此该锁可供EDT线程获取。在EDT上执行的Runnable找到可用的锁并获取它,对它调用notify,打印它的消息并退出synchronized块,释放锁。此时,主线程在退出wait方法之前获取锁,然后在存在块时释放锁


请注意,因为等待退出不是通知发生的证据,所以等待可以在没有任何通知发生的情况下退出(这是API文档中提到的虚假唤醒)。另外,虽然在这种情况下,通知代码正在等待UI控件的输入,因此主线程将首先等待,但一般来说,您不希望依赖于在通知之前发生的等待:如果通知确实首先发生,那么等待可能会无限期地继续下去。您可以通过在循环中调用wait来解决这两个问题(如上面文档中所建议的),在循环中检查通知代码设置的某些条件。

引用文档

线程释放此监视器的所有权并等待另一个监视器 线程通知等待此对象监视器唤醒的线程 通过调用notify方法或notifyAll方法。 然后线程等待,直到它可以重新获得监视器的所有权 并恢复执行

这就解释了到

starting
waiting
invoked <- could have appeared before or after waiting
由于此时EDT线程放弃同步块并释放锁,因此下面几行代码如下:

done waiting
ending main()

对锁的等待将释放该锁,并在唤醒时重新获取该锁。感谢您提供的详细信息。但愿我能接受这两个答案。:-)
calling notify
notified
done waiting
ending main()