开发者

JTextArea thread safe?

I have some code that does some initialization (including making a JTextArea object), starts three separate threads, and then these threads try to update the JTextArea (i.e. append() to it), but its not working at all. Nothing shows up on the JTextArea (however, during the initializ开发者_开发知识库ation, I print some test lines onto it, and that works fine). What's going on? How can I fix this? Also, each of those threads sleeps a random amount of time every time it has to update the JTextArea.

Sorry I haven't provided any code, its all spread out over several files.


Although I believe the API has stated that JTextArea#append(...) is thread safe, I've heard of problems with it and would recommend that this only be called on the EDT. The classic example of this is to use a SwingWorker and append to the JTextArea in the process method by calling publish.

For me, it'll be hard to make any specific suggestions to you though without code. I do have to wonder though if you're putting the EDT to sleep somewhere in your code.

Edit: as per your comment check out this tutorial: Concurrency in Swing


Edit 2: as per comment by Tim Perry, loss of thread safety and the reasoning behind this has been posted in this Java bug and which has to do with this line of code where text is added to the JTextArea's Document:

doc.insertString(doc.getLength(), str, null);

The line decomposes into two lines:

  1. int len=doc.getLength();
  2. doc.insertString(len,str,null);

The issue is that a problem can occur if the Document, doc, changes between lines 1 and 2, especially the Document length.


In Java 1.6, the documentation for JTextArea.append says:

Appends the given text to the end of the document. Does nothing if the model is null or the string is null or empty.

This method is thread safe, although most Swing methods are not. Please see How to Use Threads for more information.

In JDK7 the second part is missing:

Appends the given text to the end of the document. Does nothing if the model is null or the string is null or empty.

If you look at the Document interface (which JTextArea can use a user supplied instance), there is no way to append text in a thread-safe manner even if the implementation is thread-safe. Swing threading is just broken. I strongly suggest sticking rigidly to the AWT EDT when going anywhere near Swing components.


JTextArea.append(..) is thread safe so it should be safe to call it from different threads.

However the javadoc of .append() states:

Does nothing if the model is null or the string is null or empty.

So, make sure that the model of JTextArea is initialized (via appropriate constructor).


JTextArea thread safe?

Definitely not. Not in general. As others stated even the append method is no longer documented to be thread safe. However Java 7 documentaion of AbstractDocument.insertString clearly states that this method is thread safe.

Using AbstractDocument.insertString seems to be safe according to docs. And that's the only reasonable option. Updating string model in gui thread would be a drastic performance loss.

How about JTextArea.append? I assume it depends on the underlying Document. For PlainDocument and DefaultStyledDocument it could be thread safe. For other models one should inspect the related documentation. If one does not know what is the underlying document, then they should treat append as not thread safe and call it only from EDT.

Edit: Another possible reason for append being not thread-safe: it consists of 2 operations: getLength and insertString, and between the 2, the contents of the document may change. So be careful also with constructs like insertString(getLength(), ...). Without synchronization it's incorrect. AbstractDocument.writeLock may help, but it's protected.


I trust experienced guys who warn about believing in Document's thread safety. However it's hard to believe that an application exploits this problem so easily resulting in JTextArea displaying nothing at all. Maybe some other methods are used except append and they cause general failure. I attach a testing app, that I run on Debian with Oracle jre 6 (and also Win7 with java 6 64bit) and see no problems.

During the development process I had to fix several mistakes which included:

  1. Not synchronizing getLength() and insertString() method which resulted in wrong insertion placement and even BadLocationExceptions. Other threads were modifying the document between these 2 instructions. Even if they were on the same line :)
  2. Swallowing BadLocationException. I was sure it's impossible to hit it, but was wrong.

After realizing the above, especially the need of creating a critical section for getLength() and insertString() pair, it's obvious that JTextArea would fail (see Tom Hawtin's answer here). And I saw it actually did, because not every insertString was executed successfully and the resulting text was shorter than it should be. However this problem did not occur with loop count 10000, only at 100000. And looking into jdk 7 code of JTextArea.append, which does nothing but modify the underlying document, it seems that synchronizing JTextArea externally would also do.

Inserting at 0 also worked well, without any synchronization, although it took ages to complete.

Usually in such applications one wants to scroll to the last line. Hey, this is awt. You can't setCaretPosition out of EDT. So I don't. Scroll manually. My suggestion to solve scrolling is in another answer.

If you guys see problems with this application on your system, please comment. My conclusion is that Document.insertString is thread safe, however to use it effectively, together with getLength, synchronization is neccessary.

In the following code PlainDocument is subclassed to create synchronized append method, which wraps getLength and insertString into a lock. This lock has protected access, so I couldn't use it without a separate class. However external synchronization also gave correct results.

BTW: Sorry for so many edits. Finally I restructured this answer after learning more.

The code:

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);
        }
    });
  }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜