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 线程执行。

参考:

相关推荐
野犬寒鸦10 小时前
从零起步学习并发编程 || 第一章:初步认识进程与线程
java·服务器·后端·学习
我爱娃哈哈10 小时前
SpringBoot + Flowable + 自定义节点:可视化工作流引擎,支持请假、报销、审批全场景
java·spring boot·后端
李梨同学丶12 小时前
0201好虫子周刊
后端
思想在飞肢体在追12 小时前
Springboot项目配置Nacos
java·spring boot·后端·nacos
Loo国昌15 小时前
【垂类模型数据工程】第四阶段:高性能 Embedding 实战:从双编码器架构到 InfoNCE 损失函数详解
人工智能·后端·深度学习·自然语言处理·架构·transformer·embedding
ONE_PUNCH_Ge16 小时前
Go 语言泛型
开发语言·后端·golang
良许Linux16 小时前
DSP的选型和应用
后端·stm32·单片机·程序员·嵌入式
不光头强16 小时前
spring boot项目欢迎页设置方式
java·spring boot·后端
怪兽毕设16 小时前
基于SpringBoot的选课调查系统
java·vue.js·spring boot·后端·node.js·选课调查系统
学IT的周星星17 小时前
Spring Boot Web 开发实战:第二天,从零搭个“会卖萌”的小项目
spring boot·后端·tomcat