Page 2 of 3
29: synchronized void startTimer(Object source) {
30: this.source = source;
31: notify();
32: }
run() method for the timer thread, the real workhorse of the thread. First note that the entire run() method is synchronized, as are both the startTimer() and stopTimer() methods. This synchronization prevents multithreading race conditions by letting the startTimer() or stopTimer() methods execute only when the run() method enters either of the two wait() statements to release the monitor. After the timing thread starts, it waits indefinitely at line 47 to be notified and awakened
by the startTimer() method.46: //wait for notification from startTimer() 47: wait();
After receiving notification from the startTimer() method, the timing thread pauses again at the next statement.
49: //wait for event processing to reach the threshold, or 50: //interruption from stopTimer() 51: wait(delay);
Here, the thread waits for the specified length of time to pass. Once it reaches this statement, there are only two possible
paths of execution. The event currently being dispatched by your event queue may return from processing and interrupt the
thread, at which point an InterruptedException will be thrown and will force the thread to wrap around to the top of the loop and wait for the next event. However, if processing
takes longer than your specified delay period, your timer thread will awaken on its own and continue executing the following
code.
53: if (source instanceof Component)
54: parent =
SwingUtilities.getRoot((Component)source);
55: else if (source instanceof MenuComponent) {
56: MenuContainer mParent =
57: ((MenuComponent)source).getParent();
58: if (mParent instanceof Component)
59: parent = SwingUtilities.getRoot(
60: (Component)mParent);
61: }
62:
63: if (parent != null && parent.isShowing())
64: parent.setCursor(
65: Cursor.getPredefinedCursor(
66: Cursor.WAIT_CURSOR));
Here, you first determine whether the source is a java.awt.Component or a java.awt.MenuComponent, both of which indicate that you can eventually locate a java.awt.Window whose cursor you need to change. Note that you must handle a java.awt.MenuComponent as a special case because it does not extend java.awt.Component like the other UI classes in the AWT (Abstract Windowing Toolkit) package. Then a Swing utility method locates the window
that contains the source component. Once you have that parent window, you can change that window's cursor to an hourglass.
You can call the Component.setCursor() method outside the normal event-dispatch thread because, as a quick perusal of the java.awt.Component source code will reveal, setCursor() is a synchronized method.
You may notice that this timing thread waits until the last moment before trying to find the source object's parent window.
Although it may seem reasonable to find the parent within the startTimer() method, this particular implementation enjoys a performance advantage. Hundreds of events can flow through your queue if
you simply move the mouse cursor around a window, so you want to introduce as little processing overhead as possible from
your event queue. You can accomplish this by performing extra processing only when absolutely necessary, including finding
the source object's parent window. In your timer thread, you need to find the parent window only if the specified delay passes
and you are forced to display the hourglass cursor.