6
// 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");
            }});
        }
    }
}
4

4 回答 4

4

这显然是某种竞争条件。我认为这不像 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具有您尝试实现的功能。

于 2010-10-17T17:21:37.503 回答
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);
}
于 2012-10-09T09:35:44.137 回答
1

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

于 2010-10-18T09:59:57.697 回答
0

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

于 2016-07-04T13:42:02.020 回答