事件调度线程内的同步问题

时间:2012-05-17 11:59:16

标签: java swing synchronization thread-safety

我正在使用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. 方法开始和结束时有控制台输出
  5. 因此,如果运行此示例,即使您未关闭上一个对话框,也会看到每个循环都会弹出一个忽略同步的新对话框。我也尝试过简单的方法同步,但它具有相同的效果。

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

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

    (仅使用单独的线程而不是在事件派发线程内调用方法)

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

    这很奇怪 - 事件调度线程的特殊之处在于同步被忽略了吗?

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

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

    PS 不,我不能只在我的具体情况下使用第二个(工作)示例,因为我没有调用我想要的方法 - 它是从随机地点调用的,主要是来自标准JDK类(当从Internet上加载任何资源时 - JLabel中的图像,某些URL输入流或其他任何内容)。

1 个答案:

答案 0 :(得分:2)

来自对话框的setVisible javadoc documentation

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 synchronized块是可重入的,以下是每个invokeLater发生的情况:

  1. 调度线程正在调用showSomeLockingDialog
  2. 取消锁定或重新进入
  3. 打开窗口 - >阻止,直到关闭对话框
  4. 由于阻塞仅适用于setVisible而不适用于其他事件(即其他invokeLaters),因此您将获得指定的行为。