我在启动应用程序时使用JDialog
实例化,以多次显示消息。有时,对话框及其控件是不可见的,但可以单击。
JDialog
仅实例化一次,并且每次显示一条消息时设置为可见'true',然后设置为可见'false',直到显示下一条消息为止。
为了排除多线程相关问题,当线程创建消息并显示对话框时,我总是使用SwingUtilities.invokeLater(...)
进行ui调用。
因为它是一个庞大的项目而且我的问题与任何特定代码无关,所以我不会发布代码但会描述问题。这些问题似乎不具有可重现性,但有时会发生,因此尽管在EDT
上运行了每个相关的调用,但这可能是一个线程问题。
我做错了什么?
public class MessageHandler {
private volatile static MessageHandler messageHandler = null;
private List<Message>messages = null;
private volatile WeakReference<MessagesPanelControl> view = null;
private final Object viewSynchronizationObject = new Object();
private MessageHandler() {
messages = new ArrayList<Message>();
}
public static MessageHandler getInstance() {
MessageHandler result = messageHandler;
if (result == null) {
synchronized (MessageHandler.class) {
result = messageHandler;
if (result == null)
messageHandler = result = new MessageHandler();
}
}
return result;
}
public void registerView(MessagesPanelControl view) {
this.view = new WeakReference<MessagesPanelControl>(view);
}
public void addMessage(final Message message) {
synchronized (viewSynchronizationObject) {
messages.add(message);
Collections.sort(messages);
updateView();
}
}
private void updateView() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
synchronized (viewSynchronizationObject) {
if (view == null) {
return;
}
MessagesPanelControl mpc = view.get();
if (mpc != null) {
mpc.updateView();
}
}
}
});
}
}
在MainFrame类中,我在启动时进行了一次初始化:
MessagesPanelControl mp = new MessagesPanelControl();
MessageHandler.getInstance().registerView(mp);
LockPane messageBasicPane = new LockPane(this, mp);
然后在不同的线程中调用它来通过MessageHandler
Singleton显示消息:
MessageHandler.getInstance().addMessage(Message.getSimpleMessage("Error", "Fatal error occured", Message.MessageIcon.ERROR));
我没有发布所有细节,但是所有必要的部分都是为了理解整个问题,希望它能让它更容易理解。
MessagePanelControl
(mpc)是一个扩展JPanel
的类。它的updateView()
- 方法根据MessageHandler's
消息列表创建消息控件,如按钮,标签和图标。最后,该方法向主框架发送Delegate
类似命令,以显示包含JDialog
的{{1}}。
总结它:
MessagePanelControl
MessageHandler
messageList.size()&lt; = 0:用MessagePanelControl
隐藏JDialog
public void updateView(){ synchronized(viewMPCSynchronizationObject){ Utils.throwExceptionWhenNotOnEDT();
MessagePanelControl
}
大型机:
JPanel messagesListPanel = new JPanel();
scrollPane.setViewportView(messagesListPanel);
scrollPane.setBorder(null);
messagesListPanel.setLayout(new BoxLayout(messagesListPanel, BoxLayout.Y_AXIS));
if (MessageHandler.getInstance().getMessages() != null && MessageHandler.getInstance().getMessages().size() > 0) {
[...]
//Create buttons, text icons... for each message
[...]
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame().showMessageBoard();
}
});
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame().closeMessageBoard();
}
});
}
repaint();
}
此行详细创建 //Show Messageboard
public void showMessageBoard() {
if (messageBasicPane != null) {
messageBasicPane.setVisible(true);
messageBasicPane.repaint();
}
}
[...]
//Close Messageboard
public void closeMessageBoard() {
if (messageBasicPane != null) {
messageBasicPane.setVisible(false);
}
}
:
JDialog
[...]
public LockPane(JFrame parentFrame, JComponent componentToShow, Dimension paneSize, float opacity, ModalityType modality) {
super(parentFrame, true);
Utils.throwExceptionWhenNotOnEDT();
createDialog(paneSize, opacity, modality);
if (componentToShow != null) {
add(componentToShow);
}
pack();
}
private void createDialog(Dimension paneSize, float opacity, ModalityType modality) {
Utils.throwExceptionWhenNotOnEDT();
setUndecorated(true);
setModalityType(modality);
if (opacity < 1 && opacity >= 0)
com.sun.awt.AWTUtilities.setWindowOpacity(this, opacity);
setSize(paneSize);
setPreferredSize(paneSize);
setMaximumSize(paneSize);
setBounds(0, 0, paneSize.width, paneSize.height);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
}
[...]
的一个新观察是AWT-EventQueue没有被阻止,只有在有一段时间“等待”但没有阻塞的时候。另一个奇怪的事情是,有时我的Java VisualVM
完全透明(不可见),有时候它的白色具有所需的不透明度。
答案 0 :(得分:0)
在这个函数中,你实际上是在试图等待传递给Runnable
的{{1}} SwingUtilities.invokeLater
,invokeLater
提交给EDT来执行。您在viewSynchronizationObject
上持有的锁将阻止EDT,如果它被其他应用程序线程锁定,这在您的代码中是显而易见的,因为您已在其他几个地方使用此变量。
private void updateView() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
synchronized (viewSynchronizationObject) {
if (view == null) {
return;
}
MessagesPanelControl mpc = view.get();
if (mpc != null) {
mpc.updateView();
}
}
}
},false);
}
我们永远不应阻止EDT执行它的任务,否则我们的应用程序会冻结。请阅读我的posted answer here for details,了解如何通过EDT和EventQueue执行Swing事件和渲染任务。
虽然我们不了解您的应用程序逻辑,但您可以从synchronized (viewSynchronizationObject) {}
中删除invokeLater
,而是可以将SwingUtilities.invokeLater(new Runnable() {}
放在此同步块中:
synchronized (viewSynchronizationObject)
{
SwingUtilities.invokeLater(new Runnable() { /* your code */ } );
}