我相信有经验的人警告不要相信
Document
的线程安全性。然而,很难相信一个应用程序会如此轻易地利用这个问题,导致
JTextArea
根本没有显示任何内容。可能除了
append
之外还使用了其他方法,导致总体失效。我附上了一个测试应用程序,在Debian上运行Oracle jre 6(也在Win7上运行java 6 64位),没有发现问题。
在开发过程中,我不得不修复几个错误,包括:
- 未同步
getLength()
和insertString()
方法,导致插入位置错误甚至出现BadLocationException
。其他线程在这两个指令之间修改文档。即使它们在同一行 :)
- 吞掉
BadLocationException
。我确信这是不可能发生的,但我错了。
在意识到上述问题,特别是需要为
getLength()
和
insertString()
配对创建关键部分的需求后,很明显
JTextArea
会失败(
请参见Tom Hawtin的答案)。我确实看到了它的失败,因为并非每个
insertString
都成功执行,导致结果文本比应该的要短。然而,在循环次数为10000时,这个问题并没有发生,只有在100000时才出现。查看
jdk 7 JTextArea.append代码,它仅修改底层文档,似乎在外部同步
JTextArea
也可以解决这个问题。
在0处插入也很好用,不需要任何同步,尽管需要很长时间才能完成。
通常在这样的应用程序中,人们希望滚动到最后一行。嘿,这是awt。您不能在EDT之外设置CaretPosition。所以我不这样做。手动滚动。我解决滚动问题的建议在
另一个答案中。
如果你们在你们的系统上看到这个应用程序有问题,请评论。我的结论是,
Document.insertString
是线程安全的,但要有效地使用它,需要同步。
在下面的代码中,
PlainDocument
被子类化以创建同步的
append
方法,该方法将
getLength
和
insertString
封装到锁中。这个锁有受保护的访问权限,所以我不能在没有单独的类的情况下使用它。然而,外部同步也给出了正确的结果。
顺便说一句:对于这么多次编辑我很抱歉。最后,在学到更多知识后,我重构了这个答案。
代码:
import java.awt.*;
import java.util.concurrent.CountDownLatch;
import javax.swing.*;
import javax.swing.text.*;
class SafePlainDocument extends PlainDocument
{
public void append(String s)
{
writeLock();
try {
insertString(getLength(), s, null);
}
catch (BadLocationException e) {
e.printStackTrace();
}
finally
{
writeUnlock();
}
}
}
public class StressJText
{
public static CountDownLatch m_latch;
public static SafePlainDocument m_doc;
public static JTextArea m_ta;
static class MyThread extends Thread
{
SafePlainDocument m_doc;
JTextArea m_ta;
public MyThread(SafePlainDocument doc)
{
m_doc = doc;
}
public void run()
{
for (int i=1; i<=100000; i++) {
String s = String.format("%19s %9d\n", getName(), i);
m_doc.append(s);
}
StressJText.m_latch.countDown();
}
}
public static void main(String sArgs[])
{
System.out.println("hello");
final int cThreads = 5;
m_latch = new CountDownLatch(cThreads);
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
m_ta = new JTextArea();
m_doc = new SafePlainDocument();
m_ta.setDocument(m_doc);
m_ta.setColumns(50);
m_ta.setRows(20);
JScrollPane scrollPane = new javax.swing.JScrollPane();
scrollPane.setViewportView(m_ta);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);
for (int it=1; it<=cThreads; it++) {
MyThread t = new MyThread(m_doc);
t.start();
}
}
});
try {
m_latch.await();
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
System.out.println("tf len: " + m_ta.getText().length());
System.out.println("doc len: " + m_doc.getLength());
System.exit(0);
}
});
}
}
JTextArea
方法的线程安全 条款 已经被删除了。http://download.oracle.com/javase/7/docs/api/javax/swing/JTextArea.html - trashgod