使用invokeLater()实现文件加载线程时GUI不更新问题求助
问题分析与修复方案
核心问题
你现在的代码逻辑存在时序错误:调用loadBooks.start()后,立刻通过EventQueue.invokeLater()执行获取数据和更新UI的操作,但子线程的文件加载是耗时操作,此时子线程还没完成数据读取,loadBooks.getBook()返回的是null,自然无法更新UI。
EventQueue.invokeLater()的作用是把任务放到Swing的事件调度线程(EDT)中执行,但它不会等待后台线程完成,只是将任务加入EDT的任务队列,和你的子线程是并行执行的,所以根本等不到数据加载完成。
另外你的代码还有两个隐藏问题:
- 空的
catch块:任何加载过程中的异常都会被吞掉,你根本不知道加载失败的原因,必须添加异常打印或处理。 - 资源关闭不严谨:应该用
try-with-resources自动关闭流,避免资源泄漏。
修复方案一:修改子线程,加载完成后触发UI更新
在子线程完成数据加载后,再通过invokeLater()在EDT中更新UI,确保拿到的是已加载的数据:
修改LoadBooks类:
public class LoadBooks extends Thread{ private ArrayList<Book> books; private final Runnable onLoadFinish; // 通过回调传递UI更新逻辑 public LoadBooks(Runnable onLoadFinish) { this.onLoadFinish = onLoadFinish; } @Override public void run() { try (FileInputStream fis = new FileInputStream("SaveBooks.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { books = (ArrayList<Book>) ois.readObject(); } catch(Exception e) { e.printStackTrace(); // 打印异常,排查问题 books = new ArrayList<>(); // 异常时返回空集合,避免null } finally { // 加载完成后,在EDT执行UI更新 EventQueue.invokeLater(onLoadFinish); } } public ArrayList<Book> getBook() { return books; } }
调用代码修改为:
if(e.getSource() == loadBookButton){ LoadBooks loadBooks = new LoadBooks(() -> { books = loadBooks.getBook(); updateUI(books); }); loadBooks.start(); }
修复方案二:使用SwingWorker(推荐)
Swing专门提供了SwingWorker类来处理后台任务,它内置了后台执行和EDT更新的逻辑,比手动写Thread更安全简洁:
创建LoadBooksWorker类:
import javax.swing.SwingWorker; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.util.ArrayList; public class LoadBooksWorker extends SwingWorker<ArrayList<Book>, Void> { // 后台执行数据加载,不在EDT中运行 @Override protected ArrayList<Book> doInBackground() throws Exception { try (FileInputStream fis = new FileInputStream("SaveBooks.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (ArrayList<Book>) ois.readObject(); } } // 后台任务完成后,自动在EDT中执行,用于更新UI @Override protected void done() { try { ArrayList<Book> loadedBooks = get(); // 获取后台加载的结果 books = loadedBooks; updateUI(books); } catch (Exception e) { e.printStackTrace(); updateUI(new ArrayList<>()); // 异常时显示空UI } } }
调用代码简化为:
if(e.getSource() == loadBookButton){ new LoadBooksWorker().execute(); }
内容的提问来源于stack exchange,提问作者KazlLaur
相关产品推荐
相关产品推荐

