Daniel Pitts’ Tech Blog

Posts Tagged ‘SwingWorker’

Swing Concurrency

Saturday, February 9th, 2008

So many Java programmers get loured into the Swing trap. I’m not saying Swing is bad, but it has some pitfalls that many programmers fall into simply because they weren’t told otherwise.

The biggest pitfall is that most Swing classes are not thread safe, and there is a specific approach you must take to communicate with these classes.

Where there are many ways to approach thread-safety, the designers of Swing choose the “thread-partition” approach. What this means in simple terms is that any time you want to access data or methods in a Swing class, you should do it on a specific thread. This is much easier done than said.

Normally when you access an object, you use “ob.member”, for example: textField.setText("foo"). In Swing, you need to make sure your code gets called only from the Event Dispatch Thread (that’s the “single thread” as designated by Swing).

Communicating from any thread to Swing

In order to get your code to run on that thread, you can pass a Runnable to the java.awt.EventQueue. If you don’t know, Runnable is an interface in the standard Java API that has one method, public void run(); For example, in the above “foo” example, I could do this:

public void setFoo() {
   Runnable setFooRunnable = new Runnable() {
     public void run() {
       textField.setText("foo");
     }
  }
  EventQueue.invokeLater(setFooRunnable);
}

EventQueue.invokeLater tells the Event Dispatch Thread (EDT) to execute that code at its earliest convenience. invokeLater returns immediately, which may have consequences if you expect things to be done. There is another method that lets you wait until the EDT has completed your task, but it is strongly discouraged to use it.

There was a time in the past when Sun suggested that you could initialize your GUI outside of the EDT. They were wrong. Yes, I said it. Sun made a mistake. They have since sent out a correction, but unfortunately the harm was done and you’ll see some tutorials exemplifying this dangerous practice. If you so as much create an instance of a GUI object, do so on the EDT under bane of glitch. It may appear to work, but it will crash sometime, somewhere, and you might not ever hear about it from the customer who decided not to buy.

Don’t block the EDT, ever!

There is another consequence of this single thread approach. If you do something that takes a long time, and you do it on the EDT, you’ll make the whole application unresponsive. Most GUI’s, Swing included, are Event Driven, so instead of waiting for something, you should listen for that something. Most Swing components allow you to add listeners to them. The methods on your listeners get called when something they care about happens. This lets you avoid things like “while (!isFooRead()); doSomethingWithFoo(getFoo());” and instead you have “public void fooIsReady(Foo foo) { doSomethingWithFoo(foo); }“.

EDT is lazy, don’t give it hard work!

For the same reason you shouldn’t block the EDT, you should move time-consuming tasks (IO, calculations, etc…) off of the EDT. Every millisecond you take on the EDT is another millisecond that a mouse-click doesn’t get registered, or the window doesn’t repaint. This will give your program the appearance of being slow and unresponsive.

Java 1.6 has added a new class called SwingWorker that helps you pass work between a worker thread and the EDT in a clean and “correct” manor.

Where to go from here

Hopefully at this point you’ve learned the basics of what it takes to write a proper Swing program. From here, I suggest reading Concurrency in Swing from the Java tutorials. For a much more complete understanding of the why’s and how’s of Java concurrency, I suggest buying and reading Java Concurrency in Practice.