views:

272

answers:

3

In a "serious" Java GUI app, you'll have models behind many of your GUI elements: A DocumentModel backing a JEditorPane, for example, or a ListModel behind a JList.

We're always told not to make GUI changes from outside the Swing worker thread and given SwingUtilities.invoke...() for working around that. Fine, I can live with that! It's certainly necessary (and works well) when changing attributes of GUI components directly.

Ideally, most of my GUI-visible changes will be to models, not to JComponents, anyway. But because they're GUI-visible, do they "count" as GUI changes? I.e. do change events and listeners provide the necessary decoupling, or do model changes need to be wrapped in invoke...() as well?

Probably old hat to Swing pros, but I wasn't able to find any reference that clearly states one way or another.

+2  A: 

If the events are fired off the EDT and update Swing components that is going to be a problem.

In Swing text, events may or may not(!) get transferred to the EDT. This makes testing tricky. Unsurprisingly, the API is not useful in a multithreaded environment.

So: Easiest keep the model on the EDT and other threads should pass messages (involving EventQueue.invokeLater). Alternatively you can put a big lock around everything, which is more difficult (and you'll probably still to need to pass stuff to the EDT). Attempting microsynchronisation is very difficult.

Tom Hawtin - tackline
"microsynchronisation is very difficult." : I totally agree with this point. I removed all my synchronized keywords and checked more carefully that every modification on the GUI (and models of the elements of the GUI) were done on the EDT.
Laurent K
I was afraid of this. It means that my models technically become part of the GUI and have to be treated as part of it for this purpose. It doesn't seem "clean" from a layering/encapsulation point of view, and (this is my biggest objection, of course) it's more work! +1, thanks!
Carl Smotricz
+4  A: 

Generally, the model change must be wrapped into invokeLater(...). There is no decoupling in the model's code of most of the swing classes in which I looked.

It's up to you to create a model which could contain the calls checking that GUI modifications are made on the Event Dispatcher Thread.

Laurent K
-1: Not the answer I wanted to hear! (Just kidding, +1) Thank you!
Carl Smotricz
Concerning Swing thread issues debugging, I highly recommends this link:http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.htmlThe CheckThreadViolationRepaintManager saved me a lot of time.
Laurent K
Swing text does *some* shifting of events to the EDT. Of course it just makes a bigger mess.
Tom Hawtin - tackline
For all swing components and related models you have to assume they must invoked on the GUI thread - unless their documentation explicitly state that you don't. Some components/models have synchronized methods that you can modify from different threads but the majority do not.
Jeremy Raymond
A: 

Yes it is most definitely OK.

While it is true you should not modify Swing components outside of the EDT. You can certainly make changes to their models outside the EDT.

If you have wired the models to your Swing components correctly, the view updating and hence EDT scheduling will happen almost automatically.

See: http://java.sun.com/products/jfc/tsc/articles/architecture/#roots

See the part about JavaBeans Event model.

This is how models communicate their changed state to the GUI in an EDT safe manner. When creating new GUI components you should follow this design pattern.

Also note the distinction between GUI-state models and application-data models.

Making changes to models from the EDT still requires care. In fact most Swing issues happen when the programmer is modifying a model in the EDT when they should be modifying it from a separate thread. (Infamous frozen GUI problem)

Also none of this precludes being fully aware of typical multi threading pitfalls.

But you can most definitely make changes to a JTableModel from outside the EDT.

DataSurfer