【问题标题】:Wait for Swing to finish updating JProgressBar before continuing等待 Swing 完成更新 JProgressBar,然后再继续
【发布时间】:2011-10-02 00:14:13
【问题描述】:

我有一个JFrame 和一个CardLayout 设置为其布局管理器。这有两个JPanel 子类。一个是面板WordsLoadingPanel,它显示文本“Loading words...”并有一个JProgressBar。另一个必须实际加载单词。这需要一段时间(100 个单词大约需要 10-14 秒;这是一个非常有选择性的算法),所以我想向用户保证程序仍在运行。我使用firePropertyChange(String, int, int) 使面板中的加载算法触发了属性更改,WordsLoadingPanel 很好地捕捉到了更改 - 我知道这一点,因为我为此事件添加了一个侦听器来执行println,它可以工作.但是,当我将println 更改为实际更改JProgressBar 的值时,它什么也没做。我知道我正在正确更改值,因为如果我在算法开始之前设置值,它会起作用,并且它会在循环的最后一次迭代中起作用。我猜这是因为我的算法正在消耗计算能力,不会让JProgressBar 更新。

所以,我的问题是:如何让我的算法等待 Swing(这将是 AWT 调度线程吗?)在继续之前完成更新进度条?我试过了:

  • Thread.yield 在循环的每次迭代中
  • Thread.sleep(1000L) 在循环的每次迭代中,在 try/catch 中
  • 将所有内容放入循环中SwingUtilities.invokeLater(Runnable)
  • 仅将 CPU 密集型算法放入 SwingUtilities.invokeLater(Runnable)

编辑:为了进一步支持我对 CPU 消耗算法的假设(听起来像是儿童故事……),当我将 JProgressBar 设置为不确定时,它只会在算法完成后开始移动.

有人有什么建议吗?

谢谢!

【问题讨论】:

    标签: java multithreading performance swing


    【解决方案1】:

    要在后台执行昂贵的操作,请考虑使用 SwingWorker 类。该文档提供了有关如何使用它在单独的线程中执行与用户界面交互的任务的示例,包括在JProgressBars 中显示进度。

    如果您无法理解课程的运作方式,请考虑:

    • SwingWorker 是一个带有两个参数的泛型类:TV
    • doInBackground 方法返回T 并在单独的线程中执行。
    • 由于 Swing 只能在 Event Dispatch Thread 中操作,因此您不能在 doInBackground 中操作 Swing。
    • process 方法将List<V> 作为参数,并在事件调度线程上异步调用。
    • publish 方法接受V... 参数并将它们发送到process 方法中进行处理。

    总结:

    • T 是计算结果的类型,如果有的话。
    • V 是操作用户界面所需的数据类型。
    • 您的算法应该完全在 doInBackground 中运行。
    • 应在process 方法中操作用户界面。
    • 您的算法应使用publish 将数据发送到process 方法。

    【讨论】:

    • 我仍然对SwingWorker 的工作原理感到很困惑。我有一个SwingWorker<ArrayList<String>, Void> 和一个ArrayList<String> doInBackground() 和一个void process(List<Void> chunks)List<Void> 参数是什么?何时调用process?我似乎无法从您提供给我的链接(文档)或维基百科中理解这一点。你能再解释一下吗?谢谢!
    • 还请记住,这不是一项大任务,而是一百个小任务(每个大约 1/4 秒)。
    • @WChargin:如果您想使用流程/发布方法,那么 SwingWorker 的第二个通用参数不应该是 Void,而是流程传递给发布的对象类型。如果您需要更具体的帮助,请发布SSCCE,我们可以帮助您修改它,向您展示您可以尝试的内容。
    • 糟糕:这应该是由 publish 传递到 process 中的。
    • 更新了答案。希望它能让类的工作更容易理解。
    【解决方案2】:

    好的,我已经解决了。对于可能有类似问题的任何人,我的解决方案是将启动算法的方法从同步执行更改为异步执行(使用new Thread(Runnable).start)。所以,我的代码现在是

    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            new Thread(new Runnable () {
                public void run () {
                    window.keyboardTrainingPanel.initialize();                                  
                }
            }).start();
        }
    });
    

    我希望这可以帮助别人!但是,如果有更好的方法可以做到这一点,请随时通知我。

    【讨论】:

    • 你不想直接从另一个线程更新你的 UI。 swing 框架设计为单线程。您想要的是使用 Swingworker 类,它为您生成一个后台线程,但允许您将更新发布回您可以安全地更新进度条的 ui 线程。上网阅读 oracles Java 教程,或任何其他有关 Java 多线程编程的资源。
    • @HovercraftFullOfEels:是的,我会这样做;但是,请不要对我的答案投反对票 - 我在 Matheus 发布他的答案前 15 秒发布了这个(您可以通过将鼠标悬停在“已回答...之前”文本上来判断)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    • 2019-11-01
    • 2021-05-06
    • 2021-05-21
    • 2015-10-04
    • 1970-01-01
    相关资源
    最近更新 更多