【问题标题】:Implementing threading in Swing's EDT?在 Swing 的 EDT 中实现线程?
【发布时间】:2013-01-20 22:23:23
【问题描述】:

我的项目是基于 Java 的 Swing 库构建的。它生成显示我的 GUI(正常工作)的 EDT。

程序入口,初始化EDT:

public final class Main {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Start());
    }

    class Start implements Runnable {
        private Model model = new Model();
        private Controller controller = new Controller(model);
        private View view = new View(controller);

        @Override
        public void run() {
            // Initialize the view and display its JFrame...
        }
    }
}

}

但是,当在我的 GUI 中单击按钮/单选框等时,Controller 类必须对模型执行操作。

我的问题如下:

  • 我应该将控制器的代码包装在一个新的 SwingWorker 中吗?
    • 如果不是,我应该将模型的代码包装在新的 SwingWorker 中吗?
  • 如果我用线程包装控制器的代码,我是否需要在我的模型中同步共享状态变量?
  • 如果我的模型在新线程上运行,通知我的 GUI 更改,这会发生在 EDT 上还是新线程上?

例如:

public class Controller {
    public void updateModel() {
        new SwingWorker<Void, Void>() {
            @Override
            protected Void doInBackground() throws Exception {
                model.somethingSomethingSomething();
            }
        }.execute();
    }
}

public class Model {
    public void somethingSomethingSomething() {
        notifyListeners(); // This is going to notify whichever GUI 
                           // is listening to the model.
                           // Does it have to be wrapped with Swing.invokeLater?
    }
}

public class View {
    // This function is called when the model notifies its listeners.
    public void modelChangedNotifier() {
        button.setText("THE MODEL HAS CHANGED"); // Does this occur on the EDT?
    }
}

【问题讨论】:

标签: java multithreading swing swingworker


【解决方案1】:

不是从doInBackground()publish() 中间结果更新您的模型,而是从在 EDT 上执行的 process() 更新您的模型。在这个example中,JTable对应你的ViewTableModel对应你的Model。注意JTable 监听它自己的TableModel

【讨论】:

  • 对不起,我不知道为什么我最初没有理解这一点。 process()done() 都在 EDT 上执行,正是我需要的。
  • 过失;我忽略了示例的链接。
【解决方案2】:

您可以在此处阅读:Improve Application Performance With SwingWorker in Java SE 6。简而言之:所有不影响 UI 的耗时操作必须在另一个线程中完成。要显示操作结果,您必须返回 EDT。例如,如果您进行数据库搜索,您应该显示一个进度条(通常是无限的)并使用 SwingWorker 开始搜索。要在表格中显示搜索结果,您必须在 EDT 中。或者,您可以使用foxtrot lib(它可以让您的代码更方便 Swing,而无需重新设计它)。 如果您的控制器代码永久更新了 swing 小部件,您应该在 EDT 中执行它,或者至少在 EDT 中执行这些 UI 更新(使用 SwingUtilities.invokeLater,在 SwingWorker 或 swing.Timer 中进行块处理)。 所以你的示例是错误的:模型更新应该在 EDT 中是最新的。

【讨论】:

  • 如果我的视图是我的模型的表示,那么我的模型代码正在通知我的视图它的变化。控制器实际上不会影响我的按钮/标签等。我的主要问题是:当我的模型在不同的线程中更新并通知我的视图时,这是在模型的线程还是在 EDT 中完成的? (或者我对线程如何与观察者模式一起工作感到困惑......)
  • 如果您的视图包含(修改)Swing 小部件,则在 EDT 中。
  • 我还是有点困惑,你是说:i) 如果它修改小部件,我的视图应该在 EDT 上,或者 ii) 我的视图自动在 EDT 上,因为它修改了小部件?
  • Swing worker 通常用于执行 1..n 个相同的操作。如果是这样 - 您应该覆盖 done 或 process 方法(两者都在 EDT 中执行)。如果不是 - 您应该使用 SwingUtilities.invokeLater 方法更新您的视图(如果这些更改很少)。如果小部件被频繁修改(每秒超过 5 次),您应该使用 Swing 计时器(从 EDT 获取模型中的所有更改)。
  • @sdasdadas “当我的模型在不同的线程中更新时” 这是我停下来思考的地方。隔离与控制器的线程交互。与模型和视图的所有交互都应仅在 EDT 内进行 - 恕我直言
【解决方案3】:

Java Concurrency in Practice 9.4.2 中的另一种方法使用“拆分”或“共享数据模型”。您可以在任何您想要的线程上更新您的业务模型,可能是长期运行的非 EDT 线程。但随后,无需直接调用 notifyListeners() 并担心您在哪个线程上,只需调用 myComponent.repaint(),它将在 EDT 上排队重绘请求

然后,在您的 paintComponent() 方法中的某处,您明确地从模型中获取所有新数据,通常在称为 modelToView() 的方法中

   commentTextArea.setText(myModel.getCommentText());
   fooLabel.setText(myModel.getFooText());
   ...

好处是线程不是问题,至少在某些人看来,这是“有道理的”,并且模型与视图很好地分离了。缺点是您每次都在重置所有值。因此,如果您有 100 个 JComponent,则需要设置 100 个东西。此外,视图与模型的耦合非常紧密。


工作代码示例

@MadProgrammer 和@kleopatra 是正确的,如果视图直接 包含正在更新的组件,你会得到一个“无限循环的厄运”。如需证明,请参阅

Demo_14716901_Fails

但是,如果视图与组件隔离,则可以避免无限循环。通常,更高级别的视图将包含诸如 JSplitPanes 之类的东西,持有 JScrollPanes,持有 Boxes 或更多 JPanel,持有实际的低级别组件。所以这个要求,IMO,并不是不合理的。

工作代码Demo_14716901_Works

为反对者添加了一些评论

有些人想打败 Swing。他们正在编写仪器控制代码或算法,只想完成他们的工作,而不用担心 EDT、无尽的 SwingWorkers 和 invokeLaters。这种技术可以让他们完成工作。注意到一个重要的警告,它有效。 (就我个人而言,我了解并普遍喜欢 Swing,但很多人不了解)。

虽然 Swing 组件非常适合 MVC,但它们通常处于过于微观的层面。 “真实”模型不是单个字符串,而是几十个值。 “真实”的视图不是一个单一的 JLabel,它是很多 JPanel,每个都有很多组件,结合滚动条、拆分器等。这种技术通常更适合现实世界,让程序员在更高的层次上自然地思考。

至于“不良做法”,请与 Brian Goetz、Josh Bloch 等人一起讨论。好吧,那是“诉诸权威”,但它对我有用。 :-)

【讨论】:

  • 这绝对是一个替代方案,但与我所涉及的库相比,它变得非常丑陋。
  • 是的,你要么大部分时间使用这种方法,要么根本不使用。
  • 我希望你不是建议人们应该从paintComponent 更新 UI 组件?这将导致另一个重绘请求被引发和你好厄运的无限循环:P
  • 我已经有一段时间没有进行 Swing 编码了,所以我可能有细节错误,但这个概念很有效。
  • -1 a) 从地面击败摆动机制 b) 在 paint 周期中进行更新
猜你喜欢
  • 2014-03-31
  • 2023-03-16
  • 2012-08-21
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
相关资源
最近更新 更多