简体   繁体   English

在Swing的EDT中实现线程化?

[英]Implementing threading in Swing's EDT?

My project is built upon Java's Swing library. 我的项目是基于Java的Swing库构建的。 It spawns the EDT which displays my GUI (which works correctly). 它产生了显示我的GUI(正常工作)的EDT。

The entrance to the program, which initializes the 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...
        }
    }
}

} }

However, when a button / radio box / etc. is clicked within my GUI, the Controller class must perform an action on the model. 但是,当在我的GUI中单击按钮/单选框/等时,Controller类必须对模型执行操作。

My questions are the following: 我的问题如下:

  • Should I wrap the controller's code in a new SwingWorker? 我应该将控制器的代码包装在新的SwingWorker中吗?
    • If no, should I wrap my model's code in a new SwingWorker? 如果不是,我应该将模型的代码包装在新的SwingWorker中吗?
  • If I wrap the controller's code with threads, do I need to synchronize the shared state variables within my model? 如果我用线程包装控制器的代码,我是否需要在模型中同步共享状态变量?
  • If my model, running on a new thread, notifies my GUI of changes, will this occur on the EDT or on the new thread? 如果我的模型在新线程上运行,通知我的GUI更改,这会发生在EDT还是新线程上?

For example: 例如:

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?
    }
}

You can read about it here: Improve Application Performance With SwingWorker in Java SE 6 . 您可以在此处阅读: 使用Java SE 6中的SwingWorker提高应用程序性能 In short: all time consuming operations, which are not affected UI must be done in another thread . 简而言之: 所有耗时的操作,不受影响的UI必须在另一个线程中完成 To show results of operation you must go back to EDT. 要显示操作结果,您必须返回EDT。 For example, if you make database search, you should show a progress bar (usually infinite) and start the search using SwingWorker. 例如,如果进行数据库搜索,则应显示进度条(通常为无限)并使用SwingWorker开始搜索。 To show search results in a table, you must be in EDT. 要在表格中显示搜索结果,您必须在EDT中。 Alternatively you can use foxtrot lib (it allows to make your code more Swing convenient without to redesign it). 或者你可以使用foxtrot lib (它允许你的代码更方便Swing而不需要重新设计它)。 If your controller code permanently updates the swing widgets you should execute it in EDT or at least perform these updates of UI in EDT (using SwingUtilities.invokeLater, chunk processing in SwingWorker or swing.Timer). 如果您的控制器代码永久更新swing小部件,您应该在EDT中执行它,或者至少在EDT中执行UI的这些更新(使用SwingUtilities.invokeLater,SwingWorker中的块处理或swing.Timer)。 So your sample is wrong: model update should be up-to-date in EDT. 所以你的样本是错的:模型更新应该在EDT中是最新的。

Instead of updating your model from doInBackground() , publish() interim results and update your model from process() , which executes on the EDT. 而不是从doInBackground()更新模型, publish()中间结果并从在EDT上执行的process()更新模型。 In this example , JTable corresponds to your View and TableModel corresponds to your Model . 在此示例中JTable对应于您的ViewTableModel对应于您的Model Note that JTable listens to its own TableModel . 请注意, JTable侦听自己的TableModel

One alternative approach, from Java Concurrency in Practice 9.4.2, uses a "Split" or a "Shared Data Model". 来自实践9.4.2中的Java Concurrency的一种替代方法使用“拆分”或“共享数据模型”。 You update your Business Model on whatever thread you want, likely the long-running non-EDT thread. 您可以在任何您想要的线程上更新您的业务模型,可能是长期运行的非EDT线程。 But then, instead of directly calling notifyListeners() and worrying about which thread you are on, simply call myComponent.repaint(), which will queue up a repaint request on the EDT . 但是,不是直接调用notifyListeners()并担心你所在的线程,而只需调用myComponent.repaint(),它将在EDT上排队重绘请求。

Then, somewhere in your paintComponent() method, you explicitly grab all new data from the Model, typically in a method called modelToView() 然后,在paintComponent()方法的某处,显式地从Model中获取所有新数据,通常在名为modelToView()的方法中

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

The upsides are that threading is not an issue, and, at least to some minds, this "makes sense", and the model is nicely decoupled from the view. 好处是线程不是一个问题,并且,至少对某些人来说,这“有道理”,并且模型与视图很好地分离。 A downside is that you are resetting all the values every time. 缺点是您每次都要重置所有值。 So if you have 100 JComponents, that's 100 things getting set. 所以,如果你有100个JComponents,那就是设置的100件事。 Also, the view is pretty tightly coupled to the model. 此外,视图与模型紧密耦合。


Working Code Examples 工作代码示例

@MadProgrammer and @kleopatra are correct that, if the view directly contains the components that are being updated, you get an "infinite loop of doom". @MadProgrammer和@kleopatra是正确的,如果视图直接包含正在更新的组件,则会出现“无限循环的厄运”。 For proof, see 有关证明,请参阅

Demo_14716901_Fails Demo_14716901_Fails

However, if the view is isolated from the components, you avoid the infinite loop. 但是,如果视图与组件隔离 ,则可以避免无限循环。 Normally, the higher level view would contain stuff like JSplitPanes, holding JScrollPanes, holding Boxes or more JPanels, holding the actual low level components. 通常情况下,更高级别的视图将包含JSplitPanes,持有JScrollPanes,持有Box或更多JPanels等内容,保留实际的低级别组件。 So this requirement, IMO, is not unreasonable. 所以这个要求,IMO,并非不合理。

Working code at Demo_14716901_Works Demo_14716901_Works上的工作代码

Some Comments Added for the Downvoters : 为Downvoters添加了一些评论

Some people want to defeat Swing . 有些人想打败Swing They are writing instrument control code or algorithms and just want to get their job done without worrying about the EDT, endless SwingWorkers and invokeLaters. 他们正在编写仪器控制代码或算法,只想在不担心EDT,无休止的SwingWorkers和invokeLaters的情况下完成工作。 This technique lets them get their job done. 这项技术让他们完成了自己的工作。 With the one important caveat noted, it works. 有一个重要的警告,它有效。 (Personally, I understand and generally like Swing, but many don't). (就个人而言,我理解并且通常喜欢Swing,但很多人不喜欢)。

While Swing components are nicely MVC, they are generally at far too micro a level. 虽然Swing组件很适合MVC,但它们通常都处于微观层面。 The "real" model is not a single String, it is dozens of values. “真实”模型不是单个String,而是几十个值。 The "real" view is not a single JLabel, it is many JPanels, each with many components, combined with scrollers, splitters, etc. This technique usually fits the real world better, allowing the programmer to think naturally at a higher level. “真正的”视图不是单个JLabel,它是许多JPanel,每个都有许多组件,结合了滚动条,分离器等。这种技术通常更适合现实世界,允许程序员自然地在更高层次上思考。

As far as "bad practice", take it up with Brian Goetz, Josh Bloch, etc. ok, that's "appeal to authority", but it works for me. 至于“糟糕的做法”,请与Brian Goetz,Josh Bloch等人讨论,好吧,这是“对权威的吸引力”,但它对我有用。 :-) :-)

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

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