Swing JTextArea multithreading problem - InterruptedException

You don't want to manipulate Swing objects directly from another thread, you want to post manipulations to its event queue.


You should not update ui component from other threads, you should use EventDispatchThread. Here is the solution ;

 public synchronized void log(String text) {
        Runnable  runnable = new Runnable() {
            public void run(){
                logArea.append(text);
                logArea.append("\n");
                if (logArea.getDocument().getLength() > 50000) {
                    try {
                        logArea.getDocument().remove(0, 5000);
                    } catch (BadLocationException e) {
                        log.error("Can't clean log", e);
                    }
                }
                logArea.setCaretPosition(logArea.getDocument().getLength());
            }
        }
        SwingUtilities.invokeLater(runnable);

    }

Max, you didn't mention, that you interrupt the thread. But you surely did. So your question consists actually of 2 separate questions.

append sometimes throws InterruptedException

I just fell into the same situation and don't know how to handle it. When I interrupt the thread, then Document.insertString fails throwing this kind of error.

Others are not quite right about putting all in EDT thread. JTextArea.append method is thread safe, so it needn't be wrapped. The only method you call that you should not (in work thread) is setCaretPosition. So why you accept the invokeLater answer? Probably because putting document access in one thread removed all locking problems. See AbstractDocument.writeLock open jdk code, that explains a bit this Error.

So it looks like putting Document writes in EDT thread is really necessary, but only when one wants to interrupt the thread. And as a workaround for pretty unkind AbstractDocument behaviour, that throws an Error in this case.

I came up with the following workaround for Document Error. It's not quite clean, because the thread may be unfortunately interrupted right after setting bInterrupted flag. But this may be avoided by performing Thread.interrupt() in a controlled, synchronized way.

// test the flag and clear it (interrupted() method does clear it)
boolean bInterrupted = Thread.interrupted();
m_doc.insertString(m_doc.getLength(), s, null);
// restore the original interrupted state
if (bInterrupted)
  Thread.currentThread().interrupt();

setCaretPosition method sometimes deadlocks on waiting some lock

Here is my solution for caret update. I could simply go with invokeLater, but I wanted to avoid superfluous calls, so I added an additional flag:

/** <code>true</code> when gui update scheduled. This flag is to avoid
  * multiple overlapping updates, not to call
  * <code>invokeLater</code> too frequently.
 */
private volatile boolean m_bUpdScheduled;

/** Updates output window so that the last line be visible */
protected void update()
{
  if (!m_bUpdScheduled) {
    m_bUpdScheduled = true;
    EventQueue.invokeLater(new Runnable() {
        public void run() {
          m_bUpdScheduled = false;
          try {
            m_ebOut.setCaretPosition(m_doc.getLength());
          }
          catch (IllegalArgumentException iae) {
            // doc not in sync with text field - too bad
          }
        }
    });
  }
}