我有一些代碼可以做一些初始化(包括製作一個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