
本文介绍如何使用 swingworker 和观察者模式,让后台任务类(如 secondary)在不阻塞 ui 线程的前提下,实时通知主界面更新进度条和文本内容,实现线程安全的进度反馈。
本文介绍如何使用 swingworker 和观察者模式,让后台任务类(如 secondary)在不阻塞 ui 线程的前提下,实时通知主界面更新进度条和文本内容,实现线程安全的进度反馈。
在 Swing 应用中,直接在耗时操作中同步更新 UI 组件(如 JProgressBar 或 JTextArea)不仅会导致界面冻结,还违反了 Swing 的单线程规则:所有 UI 更新必须在事件分派线程(EDT)中执行,且长时间任务不可在 EDT 中运行。因此,将 Secondary.writeInfo() 这类计算逻辑移出 EDT 并建立安全通信机制,是实现流畅进度反馈的关键。
✅ 推荐方案:SwingWorker + 自定义观察者接口
SwingWorker 是专为 Swing 设计的异步任务工具,天然支持:
- 在后台线程执行耗时逻辑(doInBackground);
- 安全地向 EDT 发送中间结果(publish/process);
- 监听进度变化(PropertyChangeListener 监听 "progress" 属性);
- 保证所有 UI 更新发生在 EDT 内。
以下为重构后的核心实现:
public class Secondary {
public interface LoadObserver {
void progressDidChange(Secondary source, int progress);
void informationDidUpdate(Secondary source, String info);
}
private final LoadObserver observer;
public Secondary(LoadObserver observer) {
this.observer = observer;
}
public void writeInfo(int n) {
SwingWorker<string string> worker = new SwingWorker() {
@Override
protected String doInBackground() throws Exception {
String result = "";
for (int i = 1; i chunks) {
if (observer != null) {
for (String chunk : chunks) {
observer.informationDidUpdate(Secondary.this, chunk);
}
}
}
};
// 监听 progress 属性变更,自动回调 observer
worker.addPropertyChangeListener(evt -> {
if ("progress".equals(evt.getPropertyName()) && observer != null) {
observer.progressDidChange(Secondary.this, worker.getProgress());
}
});
worker.execute(); // 启动异步任务
}
}</string>
在 Primary 类中,只需传入符合 LoadObserver 的匿名实现即可完成解耦:
Secondary secondary = new Secondary(new Secondary.LoadObserver() {
@Override
public void progressDidChange(Secondary source, int progress) {
progressBar.setValue(progress); // 安全:已在 EDT 中
}
@Override
public void informationDidUpdate(Secondary source, String info) {
textArea.append(" " + info); // 安全:process() 运行在 EDT
}
});
secondary.writeInfo(11); // 非阻塞调用,立即返回
⚠️ 关键注意事项
- 禁止在 doInBackground() 中直接操作任何 Swing 组件:该方法运行在后台线程,UI 更新必须通过 publish/process 或 PropertyChangeListener 转交至 EDT。
- 避免在 process() 中执行耗时操作:它虽在 EDT 中运行,但会阻塞界面响应;应只做轻量 UI 更新(如 setText()、setValue())。
- setProgress() 是线程安全的:它会触发 "progress" 属性事件,并在 EDT 中分发 —— 无需额外 SwingUtilities.invokeLater()。
- SwingWorker 实例不可复用:每次任务需创建新实例;若需连续执行,应在 done() 回调中启动下一个 worker。
- 资源清理与异常处理:生产环境建议重写 done() 方法,通过 get() 获取结果并捕获 ExecutionException,同时处理可能的取消状态(isCancelled())。
通过该设计,Secondary 保持纯粹的业务逻辑职责,Primary 专注 UI 呈现,二者通过轻量接口解耦,既满足线程安全要求,又保障了代码可维护性与扩展性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











