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

参考:

相关推荐
喵手28 分钟前
如何利用Java的Stream API提高代码的简洁度和效率?
java·后端·java ee
掘金码甲哥34 分钟前
全网最全的跨域资源共享CORS方案分析
后端
m0_4805026442 分钟前
Rust 入门 生命周期-next2 (十九)
开发语言·后端·rust
张醒言1 小时前
Protocol Buffers 中 optional 关键字的发展史
后端·rpc·protobuf
鹿鹿的布丁1 小时前
通过Lua脚本多个网关循环外呼
后端
墨子白1 小时前
application.yml 文件必须配置哇
后端
xcya1 小时前
Java ReentrantLock 核心用法
后端
用户466537015051 小时前
如何在 IntelliJ IDEA 中可视化压缩提交到生产分支
后端·github
小楓12011 小时前
MySQL數據庫開發教學(一) 基本架構
数据库·后端·mysql
天天摸鱼的java工程师2 小时前
Java 解析 JSON 文件:八年老开发的实战总结(从业务到代码)
java·后端·面试