【问题标题】:Swing modal dialog refuses to close - sometimes!Swing 模态对话框拒绝关闭 - 有时!
【发布时间】:2011-04-26 15:07:00
【问题描述】:
// This is supposed to show a modal dialog and then hide it again. In practice,
// this works about 75% of the time, and the other 25% of the time, the dialog
// stays visible.
// This is on Ubuntu 10.10, running:
// OpenJDK Runtime Environment (IcedTea6 1.9) (6b20-1.9-0ubuntu1)

// This always prints
// setVisible(true) about to happen
// setVisible(false) about to happen
// setVisible(false) has just happened
// even when the dialog stays visible.

package modalproblemdemo;

import java.awt.Frame;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;

public class Main {
    public static void main(String[] args) {
        final Dialogs d = new Dialogs();
        new Thread() {
            @Override
            public void run() {
                d.show();
                d.hide();
            }
        }.start();
    }

    static class Dialogs {
        final JDialog dialog;

        public Dialogs() {
            dialog = new JDialog((Frame) null, "Hello World", /*modal*/ true);
            dialog.setSize(400, 200);
        }

        public void show() {
            SwingUtilities.invokeLater(new Runnable() { public void run() {
                dialog.setLocationRelativeTo(null);
                System.out.println("setVisible(true) about to happen");
                dialog.setVisible(true);
            }});
        }

        public void hide() {
            SwingUtilities.invokeLater(new Runnable() { public void run() {
                System.out.println("setVisible(false) about to happen");
                dialog.setVisible(false);
                System.out.println("setVisible(false) has just happened");
            }});
        }
    }
}

【问题讨论】:

标签: java multithreading swing modal-dialog jdialog


【解决方案1】:

这显然是某种竞争条件。我认为这不像 Erick Robertson 的回答那么简单。 Dialog 的show() 代码相当复杂,它包含一些特殊的逻辑,用于从事件调度线程中调用,并且还将事件发布到事件队列中。也许发布事件的顺序会受到线程延迟的影响。

也许你需要的是SwingUtilities.invokeAndWait(),这样你就可以保证setVisible(true)在你调用setVisible(false)之前已经完成了执行。正如Skip Head所指出的,invokeAndWait将阻塞直到对话框关闭。

你为什么还需要它?

编辑:这是我对正在发生的事情的设想:

  1. 你调用 d.show() 来发布 setVisible(true) 事件
  2. 线程被调度程序置于休眠状态,EDT 启动并开始执行第一个事件
  3. EDT 在第一个任务完成并发布显示对话框的实际事件之前被踢出
  4. 您的线程执行发布 setVisible(false) 事件的 d.hide()。线程结束,EDT 开始
  5. EDT 完成第一个任务,将其显示事件放入事件队列
  6. 它进入下一个事件,瞧,它是 setVisible(false) 事件!
  7. 它打乱了对话框的整个状态,并且保持可见且无响应。

EDIT2:看起来ProgressMonitor 具有您正在尝试实现的功能。

【讨论】:

  • InvokeAndWait in show() 导致模式对话框阻塞等待关闭。我认为这是 Zarkonnen 试图通过所有额外线程来避免的。
  • 我需要它作为“请稍候,处理”模式对话框,告诉用户程序正忙于工作。它需要是模态的,以阻止用户在程序繁忙时单击按钮(因为这样 EDT 和工作线程都会在操作状态)。
  • @Zarkonnen:事件有时很短并且会导致竞争状况?尝试将这两种方法放在synchronized 块中,也许它会起作用。
  • @tulskiy ProgressMonitor 的窗口实际上不是模态的!
【解决方案2】:

所以事实证明,当您 show()/setVisible(true) 模态对话框时,会在调用 show/setVisible 时运行第二个事件分派循环。一旦你知道了,这就很有意义了。考虑到这一点,我最终得到了以下代码:

public void runBlockingTask(final String taskName, final BlockingTask bt) {
    SwingUtilities.invokeLater(new Runnable() { public void run() {
        new Thread("Worker Thread: " + taskName) {
            @Override
            public void run() {
                bt.run();
                progressDialog.setVisible(false);
            }
        }.start();
    }});
    // NB This causes the event dispatch loop to be run inside this call,
    // which is why we need  to put everything after setVisible into an
    // invokeLater.
    progressDialog.setVisible(true);
}

【讨论】:

  • +1 用于在两年后回答您自己的问题。有奉献的徽章吗?
【解决方案3】:

您可以尝试dispose() 对话框而不是隐藏它,但是如果您想再次显示它,则需要您重新构建它。

【讨论】:

    【解决方案4】:

    在某些情况下,setVisible(true) 和 setVisible(false) 之间的一点睡眠时间(100 毫秒)可以解决问题。另见https://bugs.openjdk.java.net/browse/JDK-5109571 并且当尝试使用 dispose 而不是 setVisible(false) 时,似乎没有出现竞态条件

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-11
      • 1970-01-01
      • 1970-01-01
      • 2017-08-13
      • 1970-01-01
      • 2010-10-13
      相关资源
      最近更新 更多