1

我正在使用 java.net.Authenticator 创建一个阻止对话框,该对话框在第一次通过代理建立任何连接时向用户请求代理登录名/密码。Authenticator 工作得很好,但是当我尝试同步显示输入对话框的方法时遇到了一些奇怪的问题。

这是我发现的问题的抽象工作代码示例:

private static JFrame frame;

public static void main ( String[] args )
{
    frame = new JFrame ( "Frame" );
    frame.add ( new JLabel ( "This is main application" ) );
    frame.setSize ( 500, 500 );
    frame.setLocationRelativeTo ( null );
    frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
    frame.setVisible ( true );

    // Cycling thread
    new Thread ( new Runnable ()
    {
        public void run ()
        {
            while ( true )
            {
                // Opening new dialog in a separate event dispatch thread
                SwingUtilities.invokeLater ( new Runnable ()
                {
                    public void run ()
                    {
                        showSomeLockingDialog ();
                    }
                } );

                // Wait 3 seconds before next window
                try
                {
                    Thread.sleep ( 3000 );
                }
                catch ( InterruptedException e )
                {
                    e.printStackTrace ();
                }
            }
        }
    } ).start ();
}

private static final Object lock = new Object ();

private static void showSomeLockingDialog ()
{
    synchronized ( lock )
    {
        // Output to see that this method is not finished yet
        System.out.println ( "Method start" );

        // Modal thread-blocking dialog
        JDialog dialog = new JDialog ( frame, "Lock" );
        dialog.add ( new JLabel ( "This should be blocking other dialogs" ) );
        dialog.pack ();
        dialog.setLocationRelativeTo ( null );
        dialog.setModal ( true );
        dialog.setVisible ( true );

        // Output to see that this method is not finished yet
        System.out.println ( "Method end" );
    }
}

所以基本上:

  1. 有一个可见的主框架(实际上只是为了保持 JVM 运行)
  2. 有循环线程在每个循环中请求 showSomeLockingDialog() 方法
  3. 方法在“锁定”对象上同步
  4. 方法开始和结束有控制台输出

因此,如果您运行此示例,您将看到每个循环都会弹出一个忽略同步的新对话框,即使您不关闭前一个对话框也是如此。我也尝试过简单的方法同步,但效果相同。

如果我们稍微改变调用 showSomeLockingDialog() 的方式,一切都会改变:

    new Thread ( new Runnable ()
    {
        public void run ()
        {
            showSomeLockingDialog ();
        }
    } ).start ();

(只是使用单独的线程而不是在事件调度线程中调用方法)

这样,一切都按照我期望的方式工作 - 新的对话框调用被阻止,直到之前调用的那个被关闭。

这很奇怪——事件调度线程有什么特别之处以至于同步被忽略了?

或者如果它实际上是一个错误 - 是否有任何解决方法?也许我错过了一些巨大的东西......

一些想法:在我看来,模式对话框 setVisible 方法在事件调度线程中的行为不同(否则,如果从那里调用它会阻塞整个接口)。但这对同步有何影响...

PS不,我不能只在我的特定情况下使用第二个(工作)示例,因为我没有在我想要的地方调用该方法 - 它是从随机位置调用的,主要来自标准 JDK 类(当任何资源是从 Internet 加载 - JLabel 中的图像、一些 URL 输入流或其他任何东西)。

4

1 回答 1

2

来自对话框的setVisible javadoc 文档

It is OK to call this method from the event dispatching thread because 
the toolkit ensures that other events are not blocked while this method
is blocked.

并且基于 java 同步块是可重入的,以下是每个 invokeLater 发生的情况:

  1. 调度线程正在调用showSomeLockingDialog
  2. 取锁或重新进入
  3. 打开窗口-> 阻塞直到关闭对话框

由于阻塞仅针对 setVisible 而不是针对其他事件(即其他 invokeLater),因此您将获得指定的行为。

于 2012-05-17T13:48:53.660 回答