This weekend I created an easy way to find if you are modifying Swing components on nonswing threads. At the Desktop Threading Session at JavaOne they suggested using java.lang.reflect.Proxy to proxy calls and do a check for the Event Dispatch Thread (EDT). So you would have:
class ThreadChecker implements InvocationHandler {
public Object invoke(Object proxy,
Method method, Object[] args) throws Throwable {
if (!SwingUtilities.isEventDispatchThread() {
Thread.dumpStack();
}
}
}
This works but who wants to modify all of their code to proxy things. Instead of taking your thread checking code to each class, why not have your classes come to your thread checking code? So I began to ask myself what common piece of code do all Swing classes use that I could easily intercept ... RepaintManager! It can be replaced by calling:
RepaintManager.setCurrentManager(new MyRepaintManager());
With this knowledge I created the following class just over 20 lines later:
public class ThreadCheckingRepaintManager extends RepaintManager {
public synchronized void addInvalidComponent(JComponent jComponent) {
checkThread();
super.addInvalidComponent(jComponent);
}
private void checkThread() {
if (!SwingUtilities.isEventDispatchThread()) {
System.out.println("Wrong Thread");
Thread.dumpStack();
}
}
public synchronized void addDirtyRegion(JComponent jComponent, int i,
int i1, int i2, int i3) {
checkThread();
super.addDirtyRegion(jComponent, i, i1, i2, i3);
}
}
Next I wanted to test my invention. I plugged my new repaint manager in to the application I've been developing for 3+ years and got a big fat nothing. Turns out our application plays pretty nicely with the EDT. So I created these three actionPerformed methods:
BadSwingExample:
This code performs work simulated with Thread.sleep(1000). However since the action is invoked on the EDT the Thread.sleep causes the EDT to sleep. This will lead to repaint issues. Here is a screenshot of this code being executed as the button is clicked. Notice the button is still blue. It is not allowed to repaint until after the Thread.sleep()

public void actionPerformed(ActionEvent event) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
val++;
listModel.addElement(new Integer(val));
}
}
BadThreadSwingExample:
This code offloads the work but then modifies the listModel on the nonswing thread. This can lead to unpredictable results.
public void actionPerformed(ActionEvent event) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
val++;
listModel.addElement(new Integer(val));
}
}
}).start();
}
}
GoodSwingSwingExample:
This example offloads the work from the EDT but uses invokeLater() to place the code that modifies the listModel back on the EDT.
public void actionPerformed(ActionEvent event) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < 100; i++) {
val++;
listModel.addElement(new Integer(val));
}
}
});
}
}).start();
}
After this I fired things up in IntelliJ and watched as the stacktraces started showing up when I ran the BadThreadSwing example:

Here is the source. I've included a dialog that easily allows you to turn the ThreadCheckingRepaintManager on/off while running the examples.
Followup Entry:
There is a followup entry that handles threading of nonrealized components.
Followup Entry:
This blog entry has a second followup here:
Easily Find Swing Threading Issues - Part III
Add a comment



