Java 在Swing和x27中实现线程化;美国东部夏令时?

Java 在Swing和x27中实现线程化;美国东部夏令时?,java,multithreading,swing,swingworker,Java,Multithreading,Swing,Swingworker,我的项目是基于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

我的项目是基于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中单击按钮/单选框/等时,控制器类必须对模型执行操作

我的问题如下:

  • 我应该用一个新的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?
    }
}
公共类控制器{
public void updateModel(){
新SwingWorker(){
@凌驾
受保护的Void doInBackground()引发异常{
model.somethingsomethingsomethingsome();
}
}.execute();
}
}
公共类模型{
公共无效某物某物某物(){
notifyListeners();//这将通知任何GUI
//她在听模特儿。
//它必须用Swing.invokeLater包装吗?
}
}
公共阶级观{
//当模型通知其侦听器时调用此函数。
public void modelChangedNotifier(){
button.setText(“模型已更改”);//这是否发生在EDT上?
}
}

您可以在此处阅读相关内容:。简而言之:所有不受UI影响的耗时操作必须在另一个线程中完成。要显示操作结果,必须返回EDT。例如,如果进行数据库搜索,应该显示一个进度条(通常是无限的),并使用SwingWorker启动搜索。若要在表中显示搜索结果,您必须处于EDT中。或者,您可以使用(它可以使您的代码更加方便地摆动,而无需重新设计)。 如果您的控制器代码永久更新了swing小部件,您应该在EDT中执行它,或者至少在EDT中执行这些UI更新(使用SwingUtilities.invokeLater、SwingWorker或swing.Timer中的块处理)。
所以您的示例是错误的:EDT中的模型更新应该是最新的。

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

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

好的一面是线程不是一个问题,至少对一些人来说,这“是有意义的”,并且模型与视图很好地解耦。缺点是每次都会重置所有值。所以,如果你有100个JComponents,那就有100件事情要做了。此外,视图与模型紧密耦合


工作代码示例

@MadProgrammer和@kleopatra是正确的,如果视图直接包含正在更新的组件,那么您将得到一个“无限循环的厄运”。有关证据,请参见

但是,如果视图与组件隔离,则可以避免无限循环。通常,更高级别的视图将包含诸如JSplitPanes、JScrollPanes、box或更多jpanel之类的内容,其中包含实际的低级别组件。因此,国际海事组织认为,这一要求并非不合理

工作代码在

为下层选民添加了一些评论

有些人想打败斯温。他们正在编写仪器控制代码或算法,只想完成他们的工作,而不必担心EDT、没完没了的Swingworker和调用程序。这项技术可以让他们完成工作。注意到一个重要的警告,它是有效的。(就个人而言,我理解并普遍喜欢Swing,但很多人不这么认为)

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


至于“坏习惯”,请与Brian Goetz、Josh Bloch等讨论。好吧,这是“诉诸权威”,但对我来说是有效的。:-)

而不是从
doInBackground()
更新模型,
publish()
临时结果,并从在EDT上执行的
process()
更新模型。在这种情况下,
JTable
对应于您的
视图
TableModel
对应于您的
模型
。请注意,
JTable
侦听它自己的
TableModel

如果我的视图是我的模型的表示,那么我的模型代码将通知我的视图它的更改。控制器实际上并不影响我的按钮/标签等。我的主要问题是:当我的模型在另一个线程中更新并通知我的视图时,这是在模型的线程中还是在EDT中完成的?如果您的视图包含(修改)Swing小部件,那么(或者可能我对线程如何与observer模式一起工作感到困惑…)在EDT中。我仍然有点困惑,您是说:如果它修改小部件,我的视图应该在EDT上,或者ii)我的视图自动出现在EDT上,因为它修改了小部件?Swing worker通常用于执行1..n相同的操作。如果是这样-您应该重写done或process方法(两者都在ED中执行)
   commentTextArea.setText(myModel.getCommentText());
   fooLabel.setText(myModel.getFooText());
   ...