1

我在尝试将多个字符串插入到具有 TitledBorder 的 JTextPane 时遇到了一个非常奇怪的问题(这很重要,问题似乎只发生在 TitledBorder 上,其他 Borders 或根本没有 Border 工作正常)来自线程。我的测试代码的(对于这个问题)重要部分如下所示:

JTextPane myTextPane = new JTextPane();
myTextPane.setBorder(new TitledBorder("Some title"));
StyledDocument doc = myTextPane.getStyledDocument();
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setForeground(sas, Color.BLACK);

private void insertTwoStrings()
{
    new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            docTest.insertString(docTest.getLength(), "first string ", sas);
            docTest.insertString(docTest.getLength(), "second string\n", sas);
        }
    }).start();
}

现在问题来了: insertTwoStrings() 方法有时可以正常工作,但有时它会以一种锁定整个应用程序的方式非常糟糕地失败(我必须终止进程才能关闭应用程序)。所以我在调试器中打开程序并在那里复制问题,当有问题的线程被锁定时,我暂停它并仔细查看程序计数器位置:

程序计数器的当前位置

似乎这个 synchronized(this) 是我的问题的原因。这实际上是一个错误还是我犯了某种错误?

如果有人想复制这个,要发生问题,您必须满足以下三个条件:

  1. 从 Thread将文本插入 JTextPane(即插入其 StyledDocument)
  2. 在线程中多次调用 StyledDocument.insertString(...) ,而不仅仅是一次
  3. JTextPane 必须有一个 TitledBorder

执行线程有时仍然可以工作,但时不时会失败并锁定整个程序。

4

2 回答 2

4

正如其他人已经指出的那样,除非特定方法被记录为线程安全,否则 Swing 不是线程安全的。Swing 提供了几个实用程序方法和类,可以将消息传递给事件调度线程 - SwingUtilities 包含 invokeLater 和 invokeAndWait,SwingWorker 线程可以将“完成”的工作发送到回调。

// run will be executed on the EDT
SwingUtilities.invokeLater(new Runnable()
{
    @Override
    public void run()
    {
        docTest.insertString(docTest.getLength(), "first string ", sas);
        docTest.insertString(docTest.getLength(), "second string\n", sas);
    }
});

上面的代码将在 swings 事件调度线程上执行 run,因此请注意保持 run() 简短(快速读取),否则您的用户界面将慢到爬行。

于 2012-11-17T15:50:15.757 回答
2
  • 甚至Runnable#Thread是正确的方法,但从未通知 EDT 的常见问题

表示任何带有Document, 模型的JTextComponents

docTest.insertString(docTest.getLength(), "first string ", sas);
docTest.insertString(docTest.getLength(), "second string\n", sas);
  • 您对 Swing 中的Concurency有疑问,对于使用普通或在 Swing API 中实现的"ThreadSafe"方法(setText("")例如),此问题/将/可能相同ThreadRunnable#Thread

  • 必须包裹docTest...invokeLater()

  • 不知道为什么会存在synchonized,那么可以更好地使用invokeAndWait()

  • 注意invokeAndWait()必须从 EDT 中调用,如果isEventDispatchThread之前进行测试,否则会导致(一些异常)锁定当前 GUI,并且无法重用并且在某些情况下必须关闭当前 JVM

  • 你可以使用SwingWorker,有一些方法以 EDT 为主题,例如publish()process()done()

于 2012-11-17T15:32:16.713 回答