SwingUtilities.invokeLater 详解

SwingUtilities.invokeLater 是一个在Java Swing 中用于将代码安全地提交到事件分发线程(Event Dispatch Thread,简称EDT)执行的方法 。EDT 是Swing 应用程序中负责处理GUI 事件和更新界面的线程。为了保证Swing 组件的线程安全,所有对Swing 组件的更新都必须在EDT 中进行。invokeLater 允许你在其他线程中(例如后台线程)执行代码,然后将更新UI 的操作安全地放入EDT 队列,让EDT 在适当的时候执行这些操作。

下面是使用 SwingUtilities.invokeLater 的基本步骤和示例:

  1. 创建一个 Runnable 对象:将你想要在EDT 中执行的代码封装在一个 Runnable 接口的实现类中。这个 Runnable 对象会在 invokeLater 调用后被执行。
Java 复制代码
    Runnable updateUI = new Runnable() {
        public void run() {
            // 在这里更新你的 Swing 组件
            myComponent.setText("更新后的文本");
            myComponent.repaint(); // 如果需要,重绘组件
        }
    };
  1. 调用 SwingUtilities.invokeLater()Runnable 对象作为参数传递给 SwingUtilities.invokeLater() 方法。
Java 复制代码
    SwingUtilities.invokeLater(updateUI);
  1. 方法立即返回: invokeLater 方法会立即返回,而不会等待 Runnable 对象在EDT 中执行完毕。它会将 Runnable 对象放入EDT 的事件队列,稍后由EDT 处理。

示例:

假设你有一个 JTextField 控件 myTextField,你需要在后台线程中获取一些数据,然后更新这个文本框的内容。

Java 复制代码
import javax.swing.*;
import java.awt.*;
import java.util.concurrent.ExecutionException;

public class SwingInvokeLaterExample {

    private JFrame frame;
    private JTextField myTextField;

    public SwingInvokeLaterExample() {
        frame = new JFrame("SwingUtilities.invokeLater 示例");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 200);
        frame.setLayout(new FlowLayout());

        myTextField = new JTextField(20);
        frame.add(myTextField);

        JButton button = new JButton("启动后台任务");
        button.addActionListener(e -> {
            // 启动一个后台线程
            new SwingWorker<String, Void>() {
                @Override
                protected String doInBackground() throws Exception {
                    // 模拟耗时操作
                    Thread.sleep(2000);
                    return "后台数据";
                }

                @Override
                protected void done() {
                    try {
                        String data = get();
                        // 使用 invokeLater 将更新 UI 的操作放入 EDT
                        SwingUtilities.invokeLater(() -> {
                            myTextField.setText(data);
                        });
                    } catch (InterruptedException | ExecutionException ex) {
                        ex.printStackTrace();
                    }
                }
            }.execute();
        });
        frame.add(button);

        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(SwingInvokeLaterExample::new);
    }
}

在这个例子中,doInBackground 方法模拟了一个耗时的后台操作,在 done 方法中,我们使用 SwingUtilities.invokeLatermyTextField.setText(data) 的操作放入EDT,以安全地更新UI。

总结:

SwingUtilities.invokeLater 确保了Swing 组件的线程安全,允许在非EDT 线程中执行耗时操作,并将UI 更新操作安全地提交到EDT。这是一个非常重要的Swing 编程技巧,能帮助你避免常见的UI 错误。

详细解释 EDT:

  • EDT 的作用:

    EDT 是Swing 用于处理用户界面事件(如鼠标点击、键盘输入等)和更新GUI 的专用线程。它确保了Swing 组件的线程安全,避免了多个线程同时访问和修改GUI 导致的各种问题。

  • 单线程特性:

    虽然Java 是多线程的,但Swing 的EDT 是一个单线程。所有Swing 组件的更新和事件处理都必须通过EDT 来完成,这意味着任何对Swing 组件的直接操作都必须在EDT 线程中进行。所以不要在EDT中执行耗时长的任务 ,会导致Swing程序卡顿。耗时操作放在独立的任务线程通过SwingWorker启动。

  • 线程安全问题:

    如果在非EDT 线程中直接操作Swing 组件,可能会导致各种问题,例如界面卡顿、数据不同步、甚至是程序崩溃。

  • 如何处理:

    要确保Swing 组件的线程安全,需要使用 SwingUtilities.invokeLater()SwingUtilities.invokeAndWait() 方法将需要更新Swing 组件的代码提交到EDT 线程执行。

参考:

相关推荐
tan180°6 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
优创学社27 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
why技术7 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理7 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
ai小鬼头8 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
简佐义的博客9 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
Code blocks9 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
追逐时光者10 小时前
一款开源免费、通用的 WPF 主题控件包
后端·.net