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:
- Is querying a Swing model’s state outside the EDT (strictly speaking) wrong?
- If not, how come?
- If yes, how do you handle it? By luck, or by some clever workaround? InvokeAndWait?
synchronizedorvolatile). For example I typically write my ownTableModelimplementations, typically sitting onList<X>whereXis my business object. If I intend for other threads to query the List I will make this a synchronizedCollection. It’s also worth noting I would never normally update theListfrom other threads; only query it.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
SwingWorkerand then assign the results ofdoInBackground()from within thedone()method (i.e. on the EDT). This is cleaner IMHO as thedoInBackground()method has no side-effects.