看起来你遇到了与 JEditorPane、HTMLEditorKit 以及解析方法相关的“非法阻塞”问题,这通常发生在 Swing 的 JEditorPane 尝试在 事件调度线程 (EDT) 上执行一个可能会阻塞的 I/O 操作(例如加载远程 URL 或解析大量 HTML 内容)时。

核心原因:
JEditorPane 的 setPage() 或某些解析器方法默认在调用线程上执行,如果在 EDT(Swing 的 UI 线程)上调用可能导致阻塞的操作,Java 会抛出 IllegalBlocking 异常(或类似 java.awt.IllegalComponentStateException / 阻塞警告),因为这会冻结 UI。
解决方案
在后台线程中加载内容(推荐)
不要直接在 EDT 中调用 setPage() 或触发解析,使用 SwingWorker 在后台加载内容,加载完成后更新 UI。
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.net.URL;
public class WebViewer extends JFrame {
private JEditorPane editorPane;
public WebViewer() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(800, 600);
editorPane = new JEditorPane();
editorPane.setEditable(false);
setContentPane(new JScrollPane(editorPane));
// 正确的做法:使用 SwingWorker
loadPageInBackground("http://example.com");
}
private void loadPageInBackground(final String urlString) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// 在后台线程中执行可能会阻塞的操作
URL url = new URL(urlString);
editorPane.setPage(url); // 这里不会阻塞 EDT
return null;
}
@Override
protected void done() {
// 处理可能的异常(例如网络错误)
try {
get(); // 检查 doInBackground 是否抛出了异常
} catch (Exception e) {
e.printStackTrace();
// 显示错误信息(仍在 EDT 上)
editorPane.setText("无法加载页面: " + e.getMessage());
}
}
};
worker.execute(); // 启动后台线程
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new WebViewer().setVisible(true));
}
}
检查是否正在 EDT 上调用(调试用)
如果你只是想临时绕过检查(不推荐生产环境使用),可以在调用前后打印线程信息:
System.out.println("当前线程: " + Thread.currentThread().getName());
if (SwingUtilities.isEventDispatchThread()) {
System.out.println("警告! 正在 EDT 上执行阻塞操作!");
}
避免直接使用 setPage() 的阻塞变体
- 如果你只需要显示简单的本地 HTML 字符串,使用
setText()或JEditorPane配合HTMLEditorKit。 - 对于复杂的 HTML 渲染,考虑改用
JEditorPane的read()方法,但仍需在后台执行。
使用 JDK 17+ 的 newFixedThreadPool 或 CompletableFuture(现代风格)
CompletableFuture.runAsync(() -> {
try {
URL url = new URL("http://example.com");
editorPane.setPage(url);
} catch (IOException e) {
e.printStackTrace();
}
}, SwingUtilities::invokeLater); // 注意:这仍然在后台设置,但不是最佳实践
关键点: 任何可能阻塞的 I/O(网络、文件、大内存解析)都必须放在 SwingWorker.doInBackground() 或 ExecutorService 的后台线程中,然后在 done() 或 invokeLater() 中更新 UI。
如果异常是自定义的: 如果你在代码中实现了自己的 HTMLEditorKit.Parser 方法并且手动抛出了 IllegalBlocking 异常,那说明你在解析回调中做了违规操作(例如尝试在解析器回调中调用 Swing 访问或网络请求),请确保解析器回调既不阻塞也不访问 UI。