【发布时间】:2013-11-01 15:20:54
【问题描述】:
在后台发生的进程会触发回调以提出各种问题。
在这种情况下,问题是“可以迁移您的数据吗?”,所以我必须询问用户。由于我们必须在 EDT 上完成所有 Swing 工作,因此最终看起来像这样(我只删除了 cmets,引用了我们自己的便捷方法和 allowMigration() 的参数——除此之外,其他一切都相同):
public class UserMigrationAcceptor implements MigrationAcceptor {
private final Window ownerWindow;
public UserMigrationAcceptor(Window ownerWindow) {
this.ownerWindow = ownerWindow;
}
// called on background worker thread
@Override
public boolean allowMigration() {
final AtomicBoolean result = new AtomicBoolean();
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
result.set(askUser());
}
});
} catch (InterruptedException e) {
Thread.currentThread.interrupt();
return false;
} catch (InvocationTargetException e) {
throw Throwables.propagate(e.getCause());
}
return result.get();
}
// called on EDT
private boolean askUser() {
int answer = JOptionPane.showConfirmDialog(ownerWindow, "...", "...",
JOptionPane.OK_CANCEL_OPTION);
return answer == JOptionPane.OK_OPTION;
}
}
发生的情况是,在某些情况下,在确认或取消出现的对话框后,Swing 似乎进入了以下状态:
-
JOptionPane不再可见 - 事件队列中没有任何待处理的内容
- 后台线程卡在
#invokeAndWait内部,等待InvocationEvent#isDispatched()返回true。
我们在这里做错了什么,还是我在查看 Swing/AWT 中的错误?
唯一值得注意的另一件事是,这是模态对话框的第二级。有一个显示操作进度的模式对话框,然后此确认对话框将进度对话框作为其父项。
更新 1: 以下是当前阻止 EDT 的地方:
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Unsafe.java:-1)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.awt.EventQueue.getNextEvent(EventQueue.java:543)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:154)
at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:182)
at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:221)
at java.security.AccessController.doPrivileged(AccessController.java:-1)
at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:219)
at java.awt.Dialog.show(Dialog.java:1082)
at java.awt.Component.show(Component.java:1651)
at java.awt.Component.setVisible(Component.java:1603)
at java.awt.Window.setVisible(Window.java:1014)
at java.awt.Dialog.setVisible(Dialog.java:1005)
at com.acme.swing.progress.JProgressDialog$StateChangeListener$1.run(JProgressDialog.java:200)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(AccessController.java:-1)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:154)
at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:182)
at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:221)
at java.security.AccessController.doPrivileged(AccessController.java:-1)
at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:219)
at java.awt.Dialog.show(Dialog.java:1082)
at javax.swing.JOptionPane.showOptionDialog(JOptionPane.java:870)
这里奇怪的是,底部的showOptionDialog() 是迁移提示,但更上方的Dialog#setVisible 是进度对话框。换句话说,不知何故,有时子对话框出现在父对话框之前,也许这就是破坏 Swing 的原因。
更新 2:
确实,我可以在测试程序中实现这一点,而无需使用我们自己的任何代码。虽然测试程序中的对话框定位不同,但挂起方式完全相同,只是重现性更高。 gist
【问题讨论】:
-
如需尽快获得更好的帮助,请发帖SSCCE。顺便说一句,我怀疑它冻结了,因为 a) 代码阻塞了 EDT,或者 b) 使用了
invokeAndWait而不是invokeLater。 OTOH,当您发布 SSCCE 时,我会知道更多。 -
困难,但会尝试。目前,EDT 正在等待处理新事件,但没有事件即将到来。据我所知, InvocationEvent.dispatch() 正在被调用,但 dispatched = true 行没有被第二次执行。 invokeLater 也无法使用,因为调用者希望我返回一个值。
-
基本示例似乎对我有用......我能够在没有 isse 的情况下循环超过 100 次
-
当这在生产中被报告时,我也无法以与报告问题相同的方式重现它。他们说它发生在确定对话框时,我发现它只发生在为我取消它时。它也只发生在第二次和随后的调用中,所以我认为这是某种竞争条件。同样,我的简单示例程序没有很好地模拟真正发生的事情,所以它也没有在那里发生。我不知道我需要在 100% 的情况下强制它发生的那种纳秒时间。 :(
-
该示例在这里运行良好 - OS/LAF/jdk 发生故障时的任何细节? (不是很有帮助,我知道,只是说 ;-)
标签: java swing freeze invokeandwait