我正在使用 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" );
}
}
所以基本上:
- 有一个可见的主框架(实际上只是为了保持 JVM 运行)
- 有循环线程在每个循环中请求 showSomeLockingDialog() 方法
- 方法在“锁定”对象上同步
- 方法开始和结束有控制台输出
因此,如果您运行此示例,您将看到每个循环都会弹出一个忽略同步的新对话框,即使您不关闭前一个对话框也是如此。我也尝试过简单的方法同步,但效果相同。
如果我们稍微改变调用 showSomeLockingDialog() 的方式,一切都会改变:
new Thread ( new Runnable ()
{
public void run ()
{
showSomeLockingDialog ();
}
} ).start ();
(只是使用单独的线程而不是在事件调度线程中调用方法)
这样,一切都按照我期望的方式工作 - 新的对话框调用被阻止,直到之前调用的那个被关闭。
这很奇怪——事件调度线程有什么特别之处以至于同步被忽略了?
或者如果它实际上是一个错误 - 是否有任何解决方法?也许我错过了一些巨大的东西......
一些想法:在我看来,模式对话框 setVisible 方法在事件调度线程中的行为不同(否则,如果从那里调用它会阻塞整个接口)。但这对同步有何影响...
PS不,我不能只在我的特定情况下使用第二个(工作)示例,因为我没有在我想要的地方调用该方法 - 它是从随机位置调用的,主要来自标准 JDK 类(当任何资源是从 Internet 加载 - JLabel 中的图像、一些 URL 输入流或其他任何东西)。