Java: Swing Libraries & Thread Safety
Asked Answered
H

11

23

I've often heard criticism of the lack of thread safety in the Swing libraries. Yet, I am not sure as to what I would be doing in my own code with could cause issues:

In what situations does the fact Swing is not thread safe come into play ?

What should I actively avoid doing ?

Hagiolatry answered 8/10, 2008 at 11:47 Comment(0)
N
27
  1. Never do long running tasks in response to a button, event, etc as these are on the event thread. If you block the event thread, the ENTIRE GUI will be completely unresponsive resulting in REALLY pissed off users. This is why Swing seems slow and crusty.

  2. Use Threads, Executors, and SwingWorker to run tasks NOT ON THE EDT ( event dispatch thread).

  3. Do not update or create widgets outside of the EDT. Just about the only call you can do outside of the EDT is Component.repaint(). Use SwingUtilitis.invokeLater to ensure certain code executes on the EDT.

  4. Use EDT Debug Techniques and a smart look and feel (like Substance, which checks for EDT violation)

If you follow these rules, Swing can make some very attractive and RESPONSIVE GUIs

An example of some REALLY awesome Swing UI work: Palantir Technologies. Note: I DO NOT work for them, just an example of awesome swing. Shame no public demo... Their blog is good too, sparse, but good

Ninetta answered 8/10, 2008 at 12:12 Comment(4)
There's also an invokeAndWait() method, but use invokeLater() whenever possible.Oversew
Use invokeAndWait if you want deadlocks. ;)Understandable
Agreed. Invoke and wait is very scary. Consider this: blog.palantirtech.com/2008/02/21/invokeandnotwaitingNinetta
Good points. Could you say me why is swing not thread safe like the AWT?Weigel
B
11

This is one of those questions that makes me glad I purchased Robinson & Vorobiev's book on Swing.

Anything that accesses the state of a java.awt.Component should be run inside the EDT, with three exceptions: anything specifically documented as thread-safe, such as repaint(), revalidate(), and invalidate(); any Component in a UI that has not yet been realized; and any Component in an Applet before that Applet's start() has been called.

Methods specially made thread-safe are so uncommon that it's often sufficient to simply remember the ones that are; you can also usually get away with assuming there are no such methods (it's perfectly safe to wrap a repaint call in a SwingWorker, for example).

Realized means that the Component is either a top-level container (like JFrame) on which any of setVisible(true), show(), or pack() has been called, or it has been added to a realized Component. This means it's perfectly fine to build your UI in the main() method, as many tutorial examples do, since they don't call setVisible(true) on the top-level container until every Component has been added to it, fonts and borders configured, etc.

For similar reasons, it's perfectly safe to build your applet UI in its init() method, and then call start() after it's all built.

Wrapping subsequent Component changes in Runnables to send to invokeLater() becomes easy to get right after doing it only a few times. The one thing I find annoying is reading the state of a Component (say, someTextField.getText()) from another thread. Technically, this has to be wrapped in invokeLater(), too; in practice, it can make the code ugly fast, and I often don't bother, or I'm careful to grab that information at initial event handling time (typically the right time to do it in most cases anyway).

Bucci answered 8/10, 2008 at 21:28 Comment(4)
"Component in a UI that has not yet been realized" -- no longer recommended practice. You should always work with your components on EDT, regardless of whether they are realized or not.Kadner
Assuming you're right, it makes me wonder why they'd recommend this. It's a substantial change, given how it would affect huge amounts of tutorial code. ...In fact, I can find no Java 6 tutorials making this recommendation; sample code still builds UIs in the main thread. Do you have a source for this?Bucci
@Paul 'tis true see #491823 and more. It's retrospective, and it's a pain.Chavez
Wow. Nice find. This'll affect a lot of my older code. Oddly, I haven't written much UI code in the past five years, come to think of it, and when I did, I never had a deadlock, so I never noticed.Bucci
U
8

It's not just that Swing is not thread-safe (not much is), but it's thread-hostile. If you start doing Swing stuff on a single thread (other than the EDT), then when in cases where Swing switches to the EDT (not documented) there may well be thread-safety issues. Even Swing text which aims to be thread-safe, isn't usefully thread-safe (for instance, to append to a document you first need to find the length, which might change before the insert).

So, do all Swing manipulations on the EDT. Note the EDT is not the thread the main is called on, so start your (simple) Swing applications like this boilerplate:

class MyApp {
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
            runEDT();
        }});
    }
    private static void runEDT() {
        assert java.awt.EventQueue.isDispatchThread();
        ...
Understandable answered 8/10, 2008 at 13:9 Comment(0)
C
4

An alternative to using intelligent skins like substance is to create the following utility method:

public final static void checkOnEventDispatchThread() {
    if (!SwingUtilities.isEventDispatchThread()) {
        throw new RuntimeException("This method can only be run on the EDT");
    }
}

Call it in every method you write that is required to be on the event dispatch thread. An advantage of this would be to disable and enable system wide checks very quickly, eg possibly removing this in production.

Note intelligent skins can of course provide additional coverage as well as just this.

Chavez answered 21/12, 2009 at 14:52 Comment(0)
C
3

Actively avoid doing any Swing work at all except on the event dispatching thread. Swing was written to be easy to extend and Sun decided a single-threaded model was better for this.

I have had no issues whilst following my advice above. There are some circumstances where you can 'swing' from other threads but I've never found the need.

Cherubini answered 8/10, 2008 at 12:4 Comment(0)
F
2

The phrase 'thread-unsafe' sounds like there is something inherently bad (you know... 'safe' - good; 'unsafe' - bad). The reality is that thread safety comes at a cost - threadsafe objects are often way more complex to implement (and Swing is complex enough even as it is.)

Also, thread-safety is achieved either using locking (slow) or compare-and-swap (complex) strategies. Given that the GUI interfaces with humans, which tend to be unpredictable and difficult to synchronize, many toolkits have decided to channel all events through a single event pump. This is true for Windows, Swing, SWT, GTK and probably others. Actually I don't know a single GUI toolkit which is truly thread-safe (meaning that you can manipulate its objects' internal state from any thread).

What is usually done instead is that the GUIs provide a way to cope with the thread-unsafety. As others noted, Swing has always provided the somewhat simplistic SwingUtilities.invokeLater(). Java 6 includes the excellent SwingWorker (available for previous versions from Swinglabs.org). There are also third party libraries like Foxtrot for managing threads in Swing context.

The notoriety of Swing is because the designers have taken light handed approach of assuming that the developer will do the right thing and not stall the EDT or modify components from outside the EDT. They have stated their threading policy loud and clear and it's up to the developers to follow it.

It's trivial to make each swing API to post a job to the EDT for each property-set, invalidate, etc., which would make it threadsafe, but at the cost of massive slowdowns. You can even do it yourself using AOP. For comparison, SWT throws exceptions when a component is accessed from a wrong thread.

Fjeld answered 8/10, 2008 at 11:47 Comment(0)
D
2

If you're using Java 6 then SwingWorker is definately the easiest way to deal with this.

Basically you want to make sure that anything that changes a UI is performed on the EventDispatchThread.

This can be found by using the SwingUtilities.isEventDispatchThread() method to tell you if you are in it (generally not a good idea - you should know what thread is active).

If you aren't on the EDT then you use SwingUtilities.invokeLater() and SwingUtilities.invokeAndWait() to invoke a Runnable on the EDT.

If you update UI's not on the EDT you get some incredibly strange behaviour. Personally I don't consider this a flaw of Swing, you get some nice efficiency by not having to synchronize all of the threads to provide a UI update - you just need to remember that caveat.

Dao answered 8/10, 2008 at 13:45 Comment(0)
B
1

Note that not even the model interfaces are thread safe. The size and the content are queried with separate get methods and so there is no way of synchronizing those.

Updating the state of the model from another thread allows for it to at least paint a situation where size is still bigger (table row is still in place), but the content is no longer there.

Updating state of the model always in EDT avoids these.

Brumley answered 8/10, 2008 at 11:47 Comment(0)
S
1

Here's a pattern for makng swing thread-freindly.

Sublass Action (MyAction) and make it's doAction threaded. Make the constructor take a String NAME.

Give it an abstract actionImpl() method.

Let it look like.. (pseudocode warning!)

doAction(){
new Thread(){
   public void run(){
    //kick off thread to do actionImpl().
       actionImpl();
       MyAction.this.interrupt();
   }.start();  // use a worker pool if you care about garbage.
try {
sleep(300);
Go to a busy cursor
sleep(600);
Show a busy dialog(Name) // name comes in handy here
} catch( interrupted exception){
  show normal cursor
}

You can record the time taken for the task, and next time, your dialog can show a decent estimate.

If you want to be really nice, do the sleeping in another worker thread too.

Sepia answered 9/10, 2008 at 1:0 Comment(0)
B
1

invokeLater() and invokeAndWait() really MUST be used when you are doing any interaction with GUI components from any thread that is NOT the EDT.

It may work during development, but like most concurrent bugs, you'll start to see weird exceptions come up that seem completely unrelated, and occur non-deterministly - usually spotted AFTER you've shipped by real users. Not good.

Also, you've got no confidence that your app will continue to work on future CPUs with more and more cores - which are more prone to encountering weird threading issues due to them being truely concurrent rather than just simulated by the OS.

Yes, it gets ugly wrapping every method call back into the EDT in a Runnable instance, but that's Java for you. Until we get closures, you just have to live with it.

Bemuse answered 16/10, 2008 at 6:0 Comment(0)
P
1

For more details about threading, Taming Java Threads by Allen Holub is an older book but a great read.

Holub, really promotes responsive UI and details examples and how to alleviate problems.

http://www.amazon.com/Taming-Java-Threads-Allen-Holub/dp/1893115100 http://www.holub.com/software/taming.java.threads.html

Love the "If i was king" section in the end there.

Perpetrate answered 1/5, 2009 at 6:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.