Swing JTextArea多线程问题 - InterruptedException

我有一个简单的控制台应用程序,它在几个线程中运行计算(其中10-20个)。现在我正在尝试创建一个简单的GUI,允许我选择要处理的文件并打印来自所有线程的日志。 因此,我使用JTextArea为我的日志创建了一个swing GUI,并将信息记录到日志中:
public synchronized void log(String text) {
    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());
}
但是,
setCaretPosition
方法有时会在等待某个锁时死锁,而
append
有时会抛出InterruptedException。
Exception in thread "Thread-401" java.lang.Error: Interrupted attempt to aquire write lock
at javax.swing.text.AbstractDocument.writeLock(AbstractDocument.java:1334)
at javax.swing.text.AbstractDocument.insertString(AbstractDocument.java:687)
at javax.swing.text.PlainDocument.insertString(PlainDocument.java:114)
at javax.swing.JTextArea.append(JTextArea.java:470)
at lt.quarko.aquila.scripts.ui.ScriptSessionFrame.log(ScriptSessionFrame.java:215)
我是Swing的新手,所以我无法理解我在这里做错了什么? 提前致谢。     
已邀请:
您不希望直接从另一个线程操作Swing对象,您希望将操作发布到它的事件队列。     
您不应该从其他线程更新ui组件,您应该使用EventDispatchThread。这是解决方案;
 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);

    }
    
马克斯,你没有提到,你是thread5线程。但你肯定做到了。所以你的问题实际上包含两个不同的问题。   
append
有时会抛出InterruptedException 我刚陷入同样的​​境地,不知道如何处理它。当我中断线程,然后
Document.insertString
没有抛出这种错误。 其他人并不十分正确地将所有内容放入EDT主题。
JTextArea.append
方法是线程安全的,所以不需要包装。你不应该调用的唯一方法(在工作线程中)是
setCaretPosition
。那你为什么接受
invokeLater
答案呢?可能是因为将文档访问放在一个线程中会删除所有锁定问题。参见
AbstractDocument.writeLock
打开的jdk代码,这解释了一下这个
Error
。 所以看起来在EDT线程中放置
Document
写入是非常必要的,但只有在想要中断线程时才会这样。作为一种非常不友善的行为的解决方法,在这种情况下会引发
Error
。 我为
Document
Error
提出了以下解决方法。它不是很干净,因为在设置
bInterrupted
标志后,线程可能会被中断。但这可以通过以受控的同步方式执行
Thread.interrupt()
来避免。
// 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
方法有时会在等待锁定时出现死锁 这是我的插入符号更新解决方案。我可以简单地使用
invokeLater
,但我想避免多余的调用,所以我添加了一个额外的标志:
/** <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
          }
        }
    });
  }
}
    
我知道这是一个旧帖子,但是如果对JTextArea的更新频率非常高,使用setCaretPosition()方法将视口自动滚动到底部可能会导致死锁问题。只在EDT上完成更新并没有避免问题或提供任何改进。 避免这个问题的方法: 不要尝试自动滚动,并将插入符更新策略设置为NEVER_UPDATE。可能不太理想,但在我们的主要应用程序中,我们有一个“滚动锁定”功能,因此可以在文本仍然被捕获时查看输出。 限制使用setCaretPosition():如果处理大量更新,请在X次更新后设置插入符号。我按行进行应用程序跟踪诊断,因此每次都可以延迟插入符号更新,直到捕获X行数。注意,如果限制,您仍需要将插入符号更新策略设置为NEVER_UPDATE。 如果您的JTextArea位于JScrollPane中,则直接使用垂直滚动条,并在每次更新文本区域后将其重新定位到最大位置。确保在重新定位滚动条时使用invokeLater(),以便考虑因文本添加而发生的任何大小更改。 以下是我创建的用于将文本捕获到JTextComponent(或其子类)中的类的代码片段,该类检查滚动窗格方法是否有效:
  if (autoScroll && (scrollPane != null)) {
    final JScrollBar vertical = scrollPane.getVerticalScrollBar();
    if (vertical != null) {
      appendText(doc, txt);
      java.awt.EventQueue.invokeLater(new Runnable() {
        @Override public void run() {
          vertical.setValue(vertical.getMaximum());
        }
      });
      return;
    }
  }
如果滚动窗格不可用,则使用caret-method,其中可以指定限制以最小化swing代码内部死锁的风险。     

要回复问题请先登录注册