Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If Swing models' getters aren't thread-safe, how do you handle them?

It is well known that updating a Swing GUI must be done exclusively in the EDT. Less is advertised that reading stuff from the GUI must/should also be done in the EDT. For instance, let's take ButtonModel's isSelected() method, which tells (for instance) ToggleButton's state ("down" or "up").

In every example I've seen, isSelected() is liberally queried from the main or whichever thread. But when I look at DefaultButtonModel's implementation, it's not synchronized, and the value is not volatile. So, strictly speaking, isSelected() could return garbage if it's read from any other thread than the one from which it's set (which is the EDT, when the user pushes the button). Or am I mistaken?

I originally thought about this when shocked by item #66 in Bloch's Effective Java, this example:

public class StopThread {
    private static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while(!stopRequested) i++;
            }
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

Contrary to what is seems, that program never terminates, on some machines at least. Updating the stopRequested flag from the main thread is invisible to the background thread. The situation can be fixed with synchronized getters & setters, or by setting the flag volatile.

So:

  1. Is querying a Swing model's state outside the EDT (strictly speaking) wrong?
  2. If not, how come?
  3. If yes, how do you handle it? By luck, or by some clever workaround? InvokeAndWait?
like image 937
Joonas Pulakka Avatar asked Jan 19 '10 08:01

Joonas Pulakka


People also ask

Does Swing is a thread-safe What do you mean by Swing is not thread-safe?

Most Swing object methods are not "thread safe". This means that if those (non thread safe) method are invoked from multiple threads this could result in thread interference or memory consistency errors. Only thread safe methods can be safely invoked from any thread.

Which Swing methods are thread-safe?

Many important methods of Swing components are explicitly documented as threadsafe. These include the JComponent repaint() and revalidate() methods, many methods of Swing text components, and all listener add and remove methods.

How do you make a method thread-safe?

When Thread 1 accesses the method at t1, Thread 2 may not be done with the method. So the value returned to Thread 1 is the value that has not been increased. Adding synchronized to this method will makes it thread-safe. When synchronized is added to a static method, the Class object is the object which is locked.

How do you make a thread-safe in Spring boot?

If you want to make sure your bean is thread-safe, you should go for the @RequestScope. As the name suggests, Spring binds such bean to a particular web request. Request beans aren't shared between multiple threads, hence you don't have to care about concurrency.


3 Answers

  1. No, not wrong, but as with any cross-thread communication you need to ensure you provide appropriate thread-safety mechanisms if you decide to do this (e.g. use of synchronized or volatile). For example I typically write my own TableModel implementations, typically sitting on List<X> where X is my business object. If I intend for other threads to query the List I will make this a synchronized Collection. It's also worth noting I would never normally update the List from other threads; only query it.
  2. Because it's exactly the same situation as with any other multi-threaded application.
  3. I typically make the collection synchronized (see 1).

Caveat

Despite my answer above I typically do not access models directly outside of the EDT. As Carl mentions, if the action of performing an update is invoked via some GUI action there's no need to perform any synchronization as the code is already being run by the EDT. However, if I wish to perform some background processing that will lead to the model being changed I will typically invoke a SwingWorker and then assign the results of doInBackground() from within the done() method (i.e. on the EDT). This is cleaner IMHO as the doInBackground() method has no side-effects.

like image 132
Adamski Avatar answered Oct 17 '22 00:10

Adamski


Yes it is wrong; unless both threads synchronize on the same object or use some other memory barrier, like volatile, as defined by the JMM one or the other can observe inconsistent memory contents. Period. End of story. Violating this might work on some, even many, architectures, but it will eventually bite you on the hiney.

The problem here is that with a few exceptions where you provide the model, such as what Adamski referred to, the Swing code is not going to synchronize on anything.

And it's worth noting that the JMM (Java Memory Model) has changed with JSR-133 in Java 5 in very important ways, so behavior with a J4 and earlier JVM may well yield different results than J5 and later. The revised JMM is, as you would expect, considerably improved.

like image 37
Lawrence Dol Avatar answered Oct 17 '22 00:10

Lawrence Dol


Swing is in general not only not thread-safe, but thread-hostile. However, most models, other than Swing text, are thread-agnostic. This means that you can use models in any thread, provided you use standard thread protection. Swing text was an attempt to be thread-safe but failed, and is in fact thread-hostile. Only use Documents from the Event Dispatch Thread (EDT).

The usual advice is to run potentially long running (typically blocking) tasks off the EDT. However, threading is difficult. SwingWorker is a popular way to induce bad design - avoid it. Try to keep EDT and non-EDT code separated. Try not to share mutable state.

Threading bugs are difficult. You might not see them on your machine, but customers may well do. Perhaps bugs appear due to optimisations in later versions of the JRE. Thread bugs are difficult to track. Therefore, be conservative. Even blocking the EDT briefly is better than nasty bugs.

like image 24
Tom Hawtin - tackline Avatar answered Oct 17 '22 01:10

Tom Hawtin - tackline