|
The Java Specialists' Newsletter
Issue 101 2005-01-18
Category:
GUI
Java version: Sun JDK 1.5.0_01 Causing Deadlocks in Swing Codeby Dr. Heinz M. Kabutz
Welcome to the 101st edition of The Java(tm) Specialists' Newsletter, now sent to 107
countries. That is right, we had 5 new countries since
the last edition: Four from Africa: Eritrea, Malawi,
Zambia and Senegal, and one from Europe: The Former
Yugoslav Republic of Macedonia. A special welcome
to you :)
The worst time of year to be working in Cape Town is between Christmas and
New Year. Our city grows by a few hundred thousand people, as the "Vaalies"
(South Africans who live across the Vaal river, such as in Johannesburg)
descend on Cape Town, together with lots of German and English tourists.
All work stops, and if you are the odd-one-out who is trying to get
something done, that week is the most depressing of the whole year. Since
1997, I got suckered EVERY year to work during that week, but this last
December, I refused. The result? When I arrived in Germany last week, I
was told that I looked 2 years younger :)
Would you like to really understand Java concurrency? Join us for an
in-depth study of how threading works in Java. During the course,
you will learn how to write correct and fast multi-threaded Java code.
Please
click here if you would like to learn more. Causing Deadlocks in Swing Code
My first newsletter was about
thread deadlocks that can occur in
many places of Java, including the GUI. I then presented some code that
automatically detected thread deadlocks in
Issue 93.
It is easy to demonstrate deadlocks, but up till now, I did not have an
example where incorrect coding in the Swing classes caused a deadlock.
I had seen it "in the field", several times, but I did not have a one-page
sample that demonstrated it. Many thanks to Dan Breen from USA who sent
me this code sample :)
In this code, several factors come together. I am constructing the frame
from within the static initializer block. Then, when I add the new
MyJDesktopPane to the content pane, and set it to visible,
it starts the Swing thread. However, I then construct a new
JInternalFrame (with the main thread!) and at the same time,
the Swing thread tries to call paintComponent() of
MyJDesktopPane, which then hangs up when calling
the staticMethod().
import javax.swing.*;
import java.awt.*;
public class StrangeProblem extends JFrame {
static {
new StrangeProblem();
}
private static void staticMethod() {
System.out.println("This is never reached");
}
private StrangeProblem() {
getContentPane().add(new MyJDesktopPane());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
setVisible(true);
// If commented out, program works fine, otherwise it hangs
new JInternalFrame();
}
private class MyJDesktopPane extends JDesktopPane {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("We will now call the static method...");
staticMethod();
System.out.println("Static method was called.");
}
}
public static void main(String[] args) {
}
}
You run this code by compiling it and typing
java StrangeProblem. This will load the class (before calling
main) which will call the static initializer block. You will
notice that the line "This is never reached" is never seen, and neither is
the line "Static method was called." When you dump the threads, you see:
We will now call the static method...
Full thread dump Java HotSpot(TM) Client VM (1.5.0_01-b08 mixed mode, sharing):
"AWT-EventQueue-0" prio=7 tid=0x009f87c0 nid=0x524 in Object.wait() [0x02f4f000..0x02f4fc64]
at StrangeProblem$MyJDesktopPane.paintComponent(StrangeProblem.java:26)
*snip*
- locked <0x22b37728> (a java.awt.Component$AWTTreeLock)
"main" prio=5 tid=0x00236040 nid=0xc2c waiting for monitor entry [0x0006f000..0x0006fc38]
at java.awt.Component.setFont(Unknown Source)
- waiting to lock <0x22b37728> (a java.awt.Component$AWTTreeLock)
*snip*
at javax.swing.JInternalFrame.<init>(Unknown Source)
at StrangeProblem.<init>(StrangeProblem.java:19)
at StrangeProblem.<clinit>(StrangeProblem.java:6)
What I found amazing is that this code always causes a deadlock (or
is that a livelock?) on Sun's VM's going right back to version 1.2.2_014.
Fixing the code is easy, and almost anything that we change will make the
problem go away. For example, we could only call
setVisible(true) after we finish adding
components. Then, we could construct the object from within main()
rather than the static initializer block.
However, the best change is to obey the Swing single-thread rule,
which tells us to only ever call GUI code from within the Swing thread.
In our case, we need to change the static initializer block to:
static {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new StrangeProblemFixed();
}
});
}
The lesson learnt is, when writing any GUI code, make sure that only the
Swing event thread is changing or reading the GUI. This applies even to
code that constructs a frame from within the main() function.
I have a newsletter written by Aleksey Gureev, which I will probably send
in my next issue, which offers another excellent approach to automatically
detecting thread deadlocks in the GUI.
Kind regards
Heinz
GUI Articles
Related Java Course
Discuss at The Java Specialist Club
|