【问题标题】:How to share information between GUI threads?如何在 GUI 线程之间共享信息?
【发布时间】:2012-06-10 23:16:54
【问题描述】:

我正在编写一个带有 GUI (Swing) 的客户端应用程序。我的两个类 ClientClass 和 MainFrame 正在运行不同的线程,但需要相互调用方法。 ClientClass 在应用程序生命周期中的某个时间点在 EventQueue 线程 (displayGUI()) 上实例化 GUI (MainFrame)。 ClientClass 包含许多方法,例如从客户端类线程调用的 recv(),用于更新 MainFrame。反过来,MainFrame 具有由事件触发的方法,例如按下按钮调用 ClientClass 中的方法。我假设在示例中处理按钮按下的烦人方法正在被 EventQueue 线程调用?

我很确定这种应用程序非常普遍,我很想知道其他人的见解。我有一种感觉,我正在做的事情不是线程安全的,那么我该如何修复/改进这个应用程序的当前模型?

示例代码:

MainFrame.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
}

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

【问题讨论】:

标签: java multithreading swing user-interface eventqueue


【解决方案1】:

在这种特殊情况下,至强的回答可能对您更直接有用,但作为一般负责人,您可能需要阅读Singletons

通过创建一个单例(在您所描述的情况下通常称为管理器或类似名称),您可以拥有一个执行与您的应用程序相关的“工作”的类,并拥有 GUI 线程( s) 向那个单身人士发送任务。

【讨论】:

    【解决方案2】:

    您可以使用SwingUtilities 调用 EDT 上的代码(在 UI 线程上)。

    鼠标点击是在 UI 线程上调用的 - 所以你应该在后台调用 send(如果需要很长时间不要阻塞 UI)。

    在您的 recv 方法中,如果您更改 GUI 状态(JLabel 文本等),您应该在 UI 线程上调用 mainFrame.updateElement(); - 您可以通过以下方式执行此操作:

    SwingUtilities.invokeLater(new Runnable() {... //
    

    一般来说 - 您所做的任何可能影响 GUI 元素的事情(更改文本、无效化、添加组件等)都应该在 EDT 上进行。

    以及您应该在后台执行的所有可能阻止用户界面的操作 - 生成新的 Thread

    如果您需要阻止任何事件并等待后台 - 您可以显示模态 JDialog(请记住,您应该将其隐藏在 finally 块中 - 以防万一)

    您还应该查看SwingWorker 类和tutorial

    【讨论】:

    • 感谢您的回复!无论如何在原始线程(ClientClass 线程)上调用发送?
    • 您可以使用 BlockingQueue,GUI 将在其中添加要发送的消息。主线程将从这个队列中获取消息并发送它们。
    • @JB Nizet 评论回答了您的问题。你也应该看看docs.oracle.com/javase/6/docs/api/java/util/concurrent/…
    猜你喜欢
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-04
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2021-01-27
    相关资源
    最近更新 更多