Swing modal dialog refuses to close - sometimes!

為{幸葍}努か 提交于 2019-12-05 07:23:31

It is clearly some sort of race condition. I don't think it is as simple as Erick Robertson's answer. Dialog's show() code is quite complicated, it contains some special logic for being called from the event dispatch thread and it also posts events to the event queue. Maybe the order in which events are posted is somehow affected by thread delays.

Perhaps what you need is SwingUtilities.invokeAndWait(), this way you guarantee that setVisible(true) has finished execution before you call setVisible(false). As Skip Head pointed out, invokeAndWait will block until the dialog is closed.

And why do you need it anyways?

EDIT: This is my scenario of what's happening:

  1. you call d.show() that posts setVisible(true) event
  2. the thread is put so sleep by the scheduler and EDT kicks in and starts executing the first event
  3. EDT gets kicked out before the first task has finished and posted an actual event that shows the dialog
  4. your thread executes d.hide() that posts the setVisible(false) event. The thread is finished and EDT kicks in
  5. EDT finished the first task, puts its showing event to the event queue
  6. It goes to the next event, and, voila, it is the setVisible(false) event!
  7. It messes up the whole state of the dialog and it stays visible and unresponsive.

EDIT2: Looks like ProgressMonitor has the functionality you are trying to implement.

So it turns out that what happens when you show()/setVisible(true) a modal dialog is that a second event dispatch loop is run within the call to show/setVisible. Which makes perfect sense once you know about it. With that in mind, I ended up with this code:

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);
}

You could try to dispose() the dialog instead of hiding it, but that would require you to rebuild it if you wanted to show it again.

A little sleep time (100ms) between setVisible(true) and setVisible(false) solves the problem in some cases. see also https://bugs.openjdk.java.net/browse/JDK-5109571 And when trying to use dispose instead of setVisible(false), no race condition seems to occur

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!