【问题标题】:Best practice for dealing with long running JEditorPane setText process处理长时间运行的 JEditorPane setText 进程的最佳实践
【发布时间】:2014-09-06 21:36:15
【问题描述】:

所有从用户点击以外的地方更新我的 swing GUI 的处理器都是使用 EventQueue.invokeLater 执行的(例如,从长时间运行的后台非 EDT 线程进程生成的输出)。

在我当前的场景中,我有一个 TCPIP 套接字读取后台线程进程,它返回需要更新 JEditorPane 对象的数据。我使用 JEditorPane setText 调用。问题是,将 setText 调用放在 invokeLater 例程中会冻结大文件的 GUI(示例案例测试 19,790 KB)。

我尝试解决这个问题是在非 EDT 后台线程中执行 setText 操作。这似乎解决了问题,但是,我担心最佳实践,因为 java 7(我正在使用的 JDK)中的 JEditorPane setText 不是线程安全的。

然而,通过 JDK 代码拖网,在我看来,这里执行的冗长过程是在 JDK DefaultEditorKit.read 中,并且在该方法中,唯一会影响 GUI 的代码是在 doc.insertString 调用中(除非我是错误)。现在,当您查看 JDK 的 PlainDocument.java insertString 方法时,它记录了它是线程安全的,因此人们会认为这个解决方案是合理的。

但是...

对我的应用程序进行压力测试,我在 GUI 周围进行了一些随机点击,目前正在运行一个树节点动画,并且在下面的大负载期间,它似乎确实减慢了动画速度,因此我担心我'还没有执行最佳解决方案(也非常担心未来的 JRE 会把我搞砸,因此不依赖当前线程安全的 insertString)。

我调查并看到之前有人问过“如何处理长时间运行的 JEditorPane setText”这个问题,但没有合适的答案。

问题 1) 有人对我目前的观察有想法吗?

问题 2) 有人对我如何实现另一个目标有想法吗?

注意 JEditorPane 是我在这里唯一的选择,因为我最终将支持 IDE 外观性质的动态字体。

还要注意,下面的调用是在 EventQueue.invokeLater 例程中调用的,因此最初的 editorPane 工作在 EDT 中。

public void updateBigDataEditorPane( final JEditorPane editorPane, final String inStr ) {
    // Update editor object and content.
    editorPane.setContentType( "text/plain" );
    editorPane.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
    editorPane.setDocument( editorPane.getEditorKit().createDefaultDocument() );
    // Content update. NOTE in non-EDT thread to stop GUI freeze with large content.
    new Thread( new Runnable() {
        @Override
        public void run() {
            //// synchronized
            synchronized( tabsLock ) {
                // Set content.
                editorPane.setText( inStr );
            } //// synchronized
        }
    }).start();
}

【问题讨论】:

  • 使用 SwingWorker 下载文本并使用 it's done 方法应用它...
  • @MadProgrammer SwingWorker 将应用与上述相同的原则(一旦我确定了上述的正确性,我会将其更改为使用 SwingWorker)。也就是说,setText 在后台而不是在 EDT 中完成(这本质上就是 SwingWorker 的内容,对吧?),然后 EDT 上所需的任何发布操作都在 done 方法中完成。我假设这就是您的意思,否则将 setText 放在 SwingWorker done 方法中会产生与我原来的问题代码相同的效果 - 在 EDT 中执行 setText 会冻结 GUI。想法?
  • 没有。 SwingWoker 为您提供发布/处理方法,允许您与 EDT 同步更新。事实上,您可以简单地使用 done 方法,该方法也在 EDT 中调用以更新 UI。请记住,Swing 通常不是线程安全的。
  • @MadProgrammer,但这种方法仍然会导致原来的 GUI 冻结问题。 EDT 线程上的 setText(带有大数据)冻结了 GUI。当然,除非我按照本的解决方案将大数据分解成块,并使用您建议的 SwingWorker 方法在 EDT 上同步这些块 GUI 更新。
  • 您仍然可以使用 publish 和 process 将“块”发送到 EDT 并在 doInBackground 方法仍在运行时对其进行处理

标签: java settext jeditorpane


【解决方案1】:

您可以使用 EditorPane 的文档(通过 editorPane.getDocument())并在另一个线程中执行对该文档的更改(通过 insertString(...),而不是使用 setText(...))。

通过这样做,您可以(某种程度地)动态地(意味着:在阅读时)写入内容。

作为示例(将文本从文件添加到文本窗格,而不冻结 UI):
编辑:此新代码测试,但它应该更好地证明我的建议...):

public void readFileAsync()
{
    final String fileName = "/path/to/file.txt";
    final StyledDocument doc = yourTextPane.getStyledDocument();

    Runnable r = new Runnable()
    {
        public void run()
        {
            try 
            {
               List<String> lines = Files.readAllLines(Paths.get(fileName), Charset.defaultCharset());
               for (String line : lines) 
               {
                  SwingUtilities.invokeLater(new Runnable()
                    {
                        public void run()
                        {
                           doc.insertString(doc.getLength(), line, null );
                        }
                    });
               }
            } catch (IOException e) {
               e.printStackTrace();
            }
        }
    };
    Thread t = new Thread(r);
    t.start();
} 

【讨论】:

  • 线程同步状态最近发生了变化,这可能不是线程安全的......
  • @Ben 对文档执行更改:如果通过 EDT 完成,则与最初报告的 GUI 冻结问题相同。如果在 EDT 之外完成,基本上会变成我最初提出的相同解决方案 - 唯一的区别是我提出的解决方案在 EDT 之外执行预文档插入操作(setText 中的代码和上面描述的读取)。
  • @MadProgrammer 现在在我使用的 JDK 7 版本中,文档 insertString 方法是线程安全的,如果上述解决方案确实是合理的,那么我需要调查交付我的 JDK7 的后果在后来的 JRE 上构建了代码(我不会在这里问这个问题,因为我还没有研究过这个问题,因为害怕投票失败)。
  • @Frank:我编辑了我的代码示例,以更好地阐明我的意思。
  • @Ben 谢谢 Ben,好的,我知道你在动态“感觉”方面的表现。对于这种大小的文件,我想逐行方法会给已经很长的场景增加更多的处理时间,但我知道这只是你的一个例子,所以对于我的解决方案,我会采纳你的想法并一次执行插入一个数据块(实际上就像JDK DefaultEditorKit.read 所做的那样)......我仍然想避免在做之前省略/复制 JEditorPane.setText 和 DefaultEditorKit.read 正在做的事情文档插入。
【解决方案2】:

虽然提供的答案是有益的,并且将用于解决特定的长时间运行的 EDT 线程要求,但我最终使用的解决方案如下;

  1. 创建一个新的 JEditorPane 对象
  2. 执行耗时的 setText 调用
  3. 完成后,将 GUI 上当前活动的 JEditorPane 对象替换为新创建的对象。

使用 SwingWorker 执行后台非 EDT 任务(1 和 2),然后在 EDT 线程上完成后执行第 3 步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-29
    • 2018-04-10
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    相关资源
    最近更新 更多