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

参考:

相关推荐
码外生活5 分钟前
🎥 手搓一套直播高并发环境!一个后端小白从 0 到 1 的搭建笔记
后端·spring cloud
Lyra_Infra17 分钟前
记一次麒麟 Linux 下达梦数据库 (DM8) 部署与 MySQL 命令行无界面迁移踩坑指南
数据库·后端·dba
Daemonkey20 分钟前
苦 Elasticsearch/Prometheus 久矣?试试 Rust 打造的云原生可观测新星:OpenObserve
后端
ThinkerQAQ_24 分钟前
并发编程(零):并发问题与讨论范围
后端
减瓦30 分钟前
被吹上天的 JWT,为什么主流网站一个都不用
后端
吃饱了得干活32 分钟前
Redisson 进阶实战:看门狗机制与分布式锁完全指南
java·redis·后端
ashiho44 分钟前
【Spring】RequestParam/PathVariable/RequestBody详解
java·后端·spring·servlet·springmvc
Charlie_Byte1 小时前
Hugo 相对路径图片显示问题的分析与解决
后端
Charlie_Byte1 小时前
用 conda 固定默认环境 normal
后端