我正在编写一个带有GUI(Swing)的客户端应用程序。我的两个类,ClientClass 和 MainFrame,运行不同的线程,但需要相互调用方法。ClientClass 在应用程序生命周期中的某个时间点在 EventQueue 线程 (displayGUI()) 上实例化 GUI(大型机)。ClientClass 包含许多方法,如 recv(),从客户端类线程调用,用于更新大型机。反过来,大型机具有由事件触发的方法,例如在 ClientClass 中调用方法的按钮的按下。我假设在示例中处理按钮按下的烦人方法是由 EventQueue 线程调用
的?我很确定这种应用程序非常普遍,我喜欢其他人的洞察力。我有一种感觉,我正在做的事情不是线程安全的,那么我该如何修复/改进此应用程序的当前模型?
示例代码:
大型机.java:
public MainFrame(ClientClass c) {
client = c;
// <Misc init code here>
btnSend = new JButton("Send");
btnSend.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent arg0) {
client.send("Hello!");
}
});
btnSend.setBounds(171, 120, 89, 23);
contentPane.add(btnSend);
}
public void updateElement() {
// Update of some element here, called from ClientClass
}
客户端类.java:
private MainFrame mainFrame;
public ClientClass() {
}
public void displayGUI() {
final ClientClass c = this;
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
mainFrame = new MainFrame(c);
mainFrame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void send(String msg) {
// Socket send operations here
// Currently called by the GUI's EventQueue thread?
}
public void recv() {
// Socket recv operations here
mainFrame.updateElement();
}
在这种特殊情况下,Xeon 的答案可能对您更直接有用,但作为一般原则,您可能需要阅读 Singletons。
通过创建单个单例(在您描述的情况下通常称为管理器或类似名称),您可以拥有一个执行与应用程序关联的"工作"的类,并让 GUI 线程将任务发送到该单例。
SwingUtilities
在 EDT(在 UI 线程上)上调用代码。
鼠标单击是在 UI 线程上调用的 - 因此您应该在后台调用send
(如果需要很长时间,请不要阻止 UI)。
在recv
方法上,如果您更改 GUI 状态(JLabel 文本等),则应在 UI 线程上调用mainFrame.updateElement();
- 您可以通过以下方式执行此操作:
SwingUtilities.invokeLater(new Runnable() {... //
一般来说 - 您所做的任何可能影响 GUI 元素(更改文本、失效、添加组件等)的事情都应该在 EDT 上执行。
以及所有可能阻止用户界面的事情,您应该在后台执行 - 生成新Thread
.
如果您需要阻止任何事件并等待后台 - 您可以显示模态JDialog
(请记住,您应该将其隐藏在块finally
中 - 以防万一)
您还应该查看 SwingWorker 类和教程。