本文目录导读:

这是一个中文技术报错信息(或开发者在搜索时输入的关键词组合),通常出现在 Java Swing 开发中,涉及 JEditorPane、HTMLEditorKit 和 解析器/写入 相关操作时的“挂起”或“阻塞”状态。
为了帮你解决这个问题,我将先解释这个报错可能的含义,然后提供排查和解决方案。
错误信息拆解
JEditorPane:Swing 中的一个轻量级 HTML 渲染组件。HTMLEditorKit:用于编辑/查看 HTML 的编辑器核心类。ParserMethod:你可能在自定义或继承了HTMLEditorKit的解析器方法。WritePending(写入挂起):这是关键点,在 Java NIO 或某些异步 I/O 模型中,WritePending是一个明确的状态,表示上一次写入操作尚未完成,无法开始新的写入,但在 Swing 的HTMLEditorKit上下文中,它通常意味着:- 文档(Document)正在被另一个线程修改,而你的线程试图同时写入。
- 网络流或文件流处于阻塞状态,写入操作一直没返回。
- 递归调用导致的死锁:你在
setText或write的回调中又触发了setText。
最常见的根本原因
综合来看,99% 的情况是因为跨线程操作 Swing 组件 或 在事件调度线程(EDT)上执行了阻塞操作。
具体场景:
-
场景 A:错误线程更新 UI 你在一个后台线程(
Thread、SwingWorker的doInBackground或Timer的非 EDT 回调)中直接调用了jEditorPane.setText(html)或jEditorPane.getDocument().insertString(...)。Swing 不是线程安全的,直接操作会导致内部状态(尤其是
Document和Writer)挂起。 -
场景 B:嵌套读取/写入导致流锁定 你在
HTMLEditorKit.ParserCallback的handleText或handleStartTag方法中,又去读取或写入同一个JEditorPane的文档,这会造成“在解析过程中试图修改源”的死锁。 -
场景 C:自定义流(Stream)或 Writer 阻塞 你给
editorKit.read()或write()传入了一个自己实现的InputStream或Writer,而这个实现里的write()或flush()方法因为同步(synchronized)、等待锁或者网络延迟而一直不返回。
解决方案与代码修正
✅ 通用修复:确保所有 UI 操作在 EDT 线程执行
这是最规范的解决方式,把所有对 JEditorPane 的文本设置或文档写入,用 SwingUtilities.invokeLater 包裹。
错误写法(会触发 WritePending):
// 假设 run() 在一个新线程中执行
new Thread(() -> {
String html = fetchHtmlFromInternet(); // 后台获取
jEditorPane.setText(html); // ❌ 直接写入,非 EDT 线程
}).start();
正确写法:
new Thread(() -> {
String html = fetchHtmlFromInternet(); // 后台获取(耗时操作)
SwingUtilities.invokeLater(() -> { // ✅ 切回 EDT
jEditorPane.setText(html);
});
}).start();
✅ 如果使用了 SwingWorker
SwingWorker<Void, Void> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
// 在后台线程执行耗时操作
return null;
}
@Override
protected void done() {
try {
get(); // 获取结果
jEditorPane.setText(resultHtml); // ✅ 此方法默认在 EDT 运行
} catch (Exception e) {
e.printStackTrace();
}
}
};
worker.execute();
✅ 修复递归/嵌套调用
如果你重写了 ParserCallback,
HTMLEditorKit.ParserCallback callback = new HTMLEditorKit.ParserCallback() {
@Override
public void handleText(char[] data, int pos) {
// ❌ 危险操作:在此处修改 editorPane 的内容
// jEditorPane.setText(new String(data));
}
};
解决方法:不要在回调方法中修改 JEditorPane 的文档,如果在解析后需要处理内容,把内容存到变量里,等解析完成(在 flush() 回调后)再修改。
✅ 如果使用的是自定义的 InputStream / Writer
检查是否在同一个流上进行了并发写入,或者流没有正确实现非阻塞,建议使用 PipedInputStream / PipedOutputStream 或 StringBufferInputStream(已废弃,可用 Reader)。
调试建议
-
打印堆栈跟踪: 在异常发生的 catch 块中,执行
e.printStackTrace(),查看调用链中是否有Thread-XX(非 EDT 线程)调用到了javax/swing/text/Document的写入方法。 -
检查 EDT 线程状态: 在可疑代码前加入:
System.out.println("Is EDT? " + SwingUtilities.isEventDispatchThread());如果在
setText时输出false,就说明是跨线程问题。 -
使用
EventQueue.invokeLater包裹所有setText、read、write。
| 问题场景 | 核心原因 | 解决方案 |
|---|---|---|
| 跨线程更新 | 后台线程直接操作 Swing | 使用 SwingUtilities.invokeLater |
| 解析器回调中修改 | 递归/嵌套锁定 | 不在回调中修改文档 |
| 自定义流阻塞 | 流的 write() 未正确返回 |
检查流的同步/阻塞实现 |
| NIO 的 WritePending | 旧版本 JDK 或错误用法 | 确保前一个 write 完成再调用 |
最可能的直接答案:你的代码中不是在 EDT 线程(Event Dispatch Thread)中调用 setText() 或 write() 的,请用 SwingUtilities.invokeLater 包裹即可。