繁体   English   中英

在Swing的EDT中实现线程化?

[英]Implementing threading in Swing's EDT?

我的项目是基于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 SE 6中的SwingWorker提高应用程序性能 简而言之: 所有耗时的操作,不受影响的UI必须在另一个线程中完成 要显示操作结果,您必须返回EDT。 例如,如果进行数据库搜索,则应显示进度条(通常为无限)并使用SwingWorker开始搜索。 要在表格中显示搜索结果,您必须在EDT中。 或者你可以使用foxtrot lib (它允许你的代码更方便Swing而不需要重新设计它)。 如果您的控制器代码永久更新swing小部件,您应该在EDT中执行它,或者至少在EDT中执行UI的这些更新(使用SwingUtilities.invokeLater,SwingWorker中的块处理或swing.Timer)。 所以你的样本是错的:模型更新应该在EDT中是最新的。

而不是从doInBackground()更新模型, publish()中间结果并从在EDT上执行的process()更新模型。 在此示例中JTable对应于您的ViewTableModel对应于您的Model 请注意, JTable侦听自己的TableModel

来自实践9.4.2中的Java Concurrency的一种替代方法使用“拆分”或“共享数据模型”。 您可以在任何您想要的线程上更新您的业务模型,可能是长期运行的非EDT线程。 但是,不是直接调用notifyListeners()并担心你所在的线程,而只需调用myComponent.repaint(),它将在EDT上排队重绘请求。

然后,在paintComponent()方法的某处,显式地从Model中获取所有新数据,通常在名为modelToView()的方法中

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

好处是线程不是一个问题,并且,至少对某些人来说,这“有道理”,并且模型与视图很好地分离。 缺点是您每次都要重置所有值。 所以,如果你有100个JComponents,那就是设置的100件事。 此外,视图与模型紧密耦合。


工作代码示例

@MadProgrammer和@kleopatra是正确的,如果视图直接包含正在更新的组件,则会出现“无限循环的厄运”。 有关证明,请参阅

Demo_14716901_Fails

但是,如果视图与组件隔离 ,则可以避免无限循环。 通常情况下,更高级别的视图将包含JSplitPanes,持有JScrollPanes,持有Box或更多JPanels等内容,保留实际的低级别组件。 所以这个要求,IMO,并非不合理。

Demo_14716901_Works上的工作代码

为Downvoters添加了一些评论

有些人想打败Swing 他们正在编写仪器控制代码或算法,只想在不担心EDT,无休止的SwingWorkers和invokeLaters的情况下完成工作。 这项技术让他们完成了自己的工作。 有一个重要的警告,它有效。 (就个人而言,我理解并且通常喜欢Swing,但很多人不喜欢)。

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

至于“糟糕的做法”,请与Brian Goetz,Josh Bloch等人讨论,好吧,这是“对权威的吸引力”,但它对我有用。 :-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM