Я доверяю опытным парням, которые предупреждают о вере в безопасность потоков Document
. Однако трудно поверить, что приложение так легко использует эту проблему, в результате чего JTextArea
вообще ничего не отображает. Возможно, используются другие методы, кроме append
, и они вызывают общий сбой. Я прилагаю тестовое приложение, которое я запускаю на Debian с Oracle jre 6 (а также Win7 с java 6 64bit) и не вижу проблем.
В процессе разработки мне пришлось исправить несколько ошибок, в том числе:
- Не синхронизируется метод
getLength()
и insertString()
, что привело к неправильному размещению вставки и даже BadLocationException
s. Другие темы изменяли документ между этими двумя инструкциями. Даже если бы они были на одной линии:)
- Глотание
BadLocationException
. Я был уверен, что поразить его невозможно, но ошибся.
После осознания вышеизложенного, особенно необходимости создания критической секции для пары getLength()
и insertString()
, становится очевидным, что JTextArea
потерпит неудачу ( см. Ответ Тома Хоутина здесь ). И я увидел, что это действительно так, потому что не все insertString
были выполнены успешно, а полученный текст был короче, чем должен быть. Однако эта проблема не возникла с числом циклов 10000, только на 100000. И, глядя на jdk 7 код JTextArea.append , который не делает ничего, кроме изменения базового документа, кажется, что внешняя синхронизация JTextArea
будет также сделай.
Вставка в 0 также работала хорошо, без какой-либо синхронизации, хотя для ее завершения потребовался возраст.
Обычно в таких приложениях хочется прокрутить до последней строки. Привет это Вы не можете setCaretPosition
из EDT. Так что я не Прокрутите вручную. Мое предложение решить прокрутку в другой ответ .
Если вы, ребята, видите проблемы с этим приложением в вашей системе, пожалуйста, прокомментируйте. Мой вывод таков: 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);
}
});
}
}