我有一些代码可以做一些初始化(包括制作一个JTextArea
对象),启动三个独立的线程,然后这些线程尝试更新JTextArea
(即append()
),但它根本不工作。 JTextArea
没有任何显示(但是,在初始化期间,我打印了一些测试线,并且工作正常)。这是怎么回事?我怎样才能解决这个问题?此外,每次线程都会随机调整时间,以便每次更新JTextArea
。JTextArea线程安全吗?
对不起,我没有提供任何代码,它的全部分布在几个文件。
我有一些代码可以做一些初始化(包括制作一个JTextArea
对象),启动三个独立的线程,然后这些线程尝试更新JTextArea
(即append()
),但它根本不工作。 JTextArea
没有任何显示(但是,在初始化期间,我打印了一些测试线,并且工作正常)。这是怎么回事?我怎样才能解决这个问题?此外,每次线程都会随机调整时间,以便每次更新JTextArea
。JTextArea线程安全吗?
对不起,我没有提供任何代码,它的全部分布在几个文件。
尽管我相信API已经声明JTextArea#append(...)是线程安全的,但我听说它有问题,并且会建议仅在EDT上调用它。这个的典型例子是使用SwingWorker并通过调用publish将其附加到process方法中的JTextArea。
对我来说,虽然没有代码,但很难向你提出任何具体的建议。我不得不怀疑,如果你让美国东部时间在你的代码的某个地方睡觉。
编辑:根据您的评论看看这个教程:Concurrency in Swing
编辑2:Tim Perry按照评论,线程安全,这背后的原因的损失已经张贴在this Java bug并具有做这行代码,其中的文字添加到JTextArea中的文件:
doc.insertString(doc.getLength(), str, null);
该生产线分解成两行:
int len=doc.getLength();
doc.insertString(len,str,null);
的问题是,可能会出现如果文档,文档,线1和2之间变化,特别是文献长度的问题。
JTextArea.append(..)
是线程安全的,因此从不同的线程调用它应该是安全的。
然而.append()
状态的Javadoc:
Does nothing if the model is null or the string is null or empty.
因此,确保JTextArea中的模型(通过适当的构造函数)初始化。
在Java 1.6,为JTextArea.append
的文件说:
将给定文本追加到 文档末尾。如果 模型为空或字符串为空或 为空,则不执行任何操作。
这种方法是线程安全的, 虽然大多数Swing方法不是。 请参阅如何使用主题获取更多 信息。
在JDK7第二部分丢失:
将给定文本追加到的 文档的结束。如果 模型为空或字符串为空或 为空,则不执行任何操作。
如果你看一下Document
接口(其中JTextArea
可以使用用户提供的实例),有没有办法在追加即使实现是线程安全的一个线程安全的方式文本。 Swing线程刚刚打破。我强烈建议在走到Swing组件附近的任何地方时严格遵守AWT EDT。
我相信有经验的人会相信Document
的线程安全。然而,很难相信应用程序如此轻易地利用这个问题,导致JTextArea
什么都没有显示。也许使用其他方法,除了append
,它们会导致一般故障。我附加了一个测试应用程序,我使用Oracle jre 6运行Debian(也使用java 6 64bit的Win7),并且没有发现任何问题。
在开发过程中,我不得不修正一些错误,其中包括:
getLength()
和insertString()
方法,这就造成了误插位置,甚至BadLocationException
秒。其他线程正在修改这两条指令之间的文件。即使他们在同一条线上:)BadLocationException
。我确定无法击中它,但是错了。实现了getLength()
和insertString()
对创建临界区的上方,尤其需要后,很明显,JTextArea
会失败(see Tom Hawtin's answer here)。而且我看到它实际上是这样做的,因为不是每个insertString
都成功执行,并且结果文本比它应该短。然而,这个问题并没有发生在循环计数10000,仅在100000处。并且调查jdk 7 code of JTextArea.append,它只做了修改底层文档,看起来外部同步JTextArea
也会这样。
虽然花费了很长时间才完成,但在0时插入也很好,没有任何同步。
通常在这样的应用程序中想要滚动到最后一行。嘿,这是awt。您无法在EDT中使用setCaretPosition
。所以我不这样做。手动滚动。我的建议解决滚动是在another answer。
如果你们在你的系统上看到这个应用程序的问题,请评论。我的结论是,Document.insertString
是线程安全的,但要有效地使用它,连同getLength
,同步是必要的。
在下面的代码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
线程安全的?
绝对不是。不一般。正如其他人所说,即使append
方法不再被记录为线程安全。但是Java 7 documentaion of AbstractDocument.insertString明确指出这种方法是线程安全的。根据文档,使用AbstractDocument.insertString
似乎是安全的。这是唯一合理的选择。在gui线程中更新字符串模型将是一个巨大的性能损失。
JTextArea.append
怎么样?我认为这取决于底层的Document
。对于PlainDocument
和DefaultStyledDocument
它可能是线程安全的。对于其他型号,应该检查相关文件。如果不知道底层文档是什么,那么他们应该将append
视为不是线程安全的,并且只能从EDT中调用它。
编辑:另一种可能原因是append
不是线程安全的:它由2个操作:getLength
和insertString
和2之间,该文件的内容可能改变。所以还要注意像insertString(getLength(), ...)
这样的结构。没有同步,这是不正确的。 AbstractDocument.writeLock
可能会有帮助,但它是受保护的。
你可以提到任何解释EDT的优秀教程吗? – 2011-02-01 23:14:01
请参阅我的答案中的编辑。 – 2011-02-01 23:15:32
非常感谢:) – 2011-02-01 23:24:11